import Store from "../store";
import {
	replaceHtml,
	getObjType,
	chatatABC,
	luckysheetactiveCell,
} from "../utils/util";
import {
	getSheetIndex,
	getconfig,
	getluckysheet_select_save,
	getluckysheetfile,
} from "../methods/get";
import locale from "../locale/locale";
import method from "./method";
import formula from "./formula";
import func_methods from "./func_methods";
import tooltip from "./tooltip";
import json from "./json";
import editor from "./editor";
import luckysheetformula from "./formula";
import cleargridelement from "./cleargridelement";
import { genarate, update } from "./format";
import { setAccuracy, setcellvalue } from "./setdata";
import { orderbydata } from "./sort";
import { rowlenByRange } from "./getRowlen";
import { getdatabyselection, getcellvalue } from "./getdata";
import {
	luckysheetrefreshgrid,
	jfrefreshgrid,
	jfrefreshgrid_rhcw,
} from "./refresh";
import {
	luckysheetDeleteCell,
	luckysheetextendtable,
	luckysheetdeletetable,
} from "./extend";
import {
	isRealNull,
	valueIsError,
	isRealNum,
	isEditMode,
	hasPartMC,
} from "./validate";
import { isdatetime, diff } from "./datecontroll";
import { getBorderInfoCompute } from "./border";
import { luckysheetDrawMain } from "./draw";
import pivotTable from "../controllers/pivotTable";
import server from "../controllers/server";
import menuButton from "../controllers/menuButton";
import selection from "../controllers/selection";
import luckysheetConfigsetting from "../controllers/luckysheetConfigsetting";
import luckysheetFreezen from "../controllers/freezen";
import luckysheetsizeauto from "../controllers/resize";
import sheetmanage from "../controllers/sheetmanage";
import conditionformat from "../controllers/conditionformat";
import { luckysheet_searcharray } from "../controllers/sheetSearch";
import { selectHightlightShow, selectIsOverlap } from "../controllers/select";
import { sheetHTML, luckysheetdefaultstyle } from "../controllers/constant";
import { createFilterOptions } from "../controllers/filter";
import controlHistory from "../controllers/controlHistory";
import { zoomRefreshView, zoomNumberDomBind } from "../controllers/zoom";
import dataVerificationCtrl from "../controllers/dataVerificationCtrl";
import imageCtrl from "../controllers/imageCtrl";
import dayjs from "dayjs";
import { getRangetxt } from "../methods/get";
import { luckysheetupdateCell } from "../controllers/updateCell";
import { generateRandomKey } from "../utils/chartUtil";
import chartInfo from "../store";
import { chartController } from "../controllers/chart";

const IDCardReg =
	/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i;

/**
 * 获取单元格的值
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {String} options.type 单元格的值类型，可以设置为原始值"v"或者显示值"m"；默认值为'v',表示获取单元格的实际值
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getCellValue(row, column, options = {}) {
	if (!isRealNum(row) || !isRealNum(column)) {
		return tooltip.info(
			"Arguments row or column cannot be null or undefined.",
			""
		);
	}
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { type = "v", order = curSheetOrder } = { ...options };
	let targetSheetData = Store.luckysheetfile[order].data;
	let cellData = targetSheetData[row][column];
	let return_v;

	if (getObjType(cellData) == "object") {
		return_v = cellData[type];

		if (type == "f" && return_v != null) {
			return_v = formula.functionHTMLGenerate(return_v);
		} else if (type == "f") {
			return_v = cellData["v"];
		} else if (cellData && cellData.ct) {
			if (cellData.ct.fa == "yyyy-MM-dd") {
				return_v = cellData.m;
			}
			// 修复当单元格内有换行获取不到值的问题
			else if (
				cellData.ct.hasOwnProperty("t") &&
				cellData.ct.t === "inlineStr"
			) {
				let inlineStrValueArr = cellData.ct.s;
				if (inlineStrValueArr) {
					return_v = inlineStrValueArr.map((i) => i.v).join("");
				}
			}
		}
	}

	if (return_v == undefined) {
		return_v = null;
	}

	return return_v;
}

/**
 * 设置单元格的值
 *
 * 关键点：如果设置了公式，则需要更新公式链insertUpdateFunctionGroup，如果设置了不是公式，判断之前是公式，则需要清除公式delFunctionGroup
 *
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object | String | Number} value 要设置的值；可以为字符串或数字，或为符合Luckysheet单元格格式的对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Boolean} options.isRefresh 是否刷新界面；默认为`true`
 * @param {Function} options.success 操作结束的回调函数
 */
export function setCellValue(row, column, value, options = {}) {
	let curv = Store.flowdata[row][column];

	// Store old value for hook function
	const oldValue = JSON.stringify(curv);

	if (!isRealNum(row) || !isRealNum(column)) {
		return tooltip.info("The row or column parameter is invalid.", "");
	}

	let {
		order = getSheetIndex(Store.currentSheetIndex),
		isRefresh = true,
		success,
	} = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	/* cell更新前触发  */
	if (
		!method.createHookFunction(
			"cellUpdateBefore",
			row,
			column,
			value,
			isRefresh
		)
	) {
		/* 如果cellUpdateBefore函数返回false 则不执行后续的更新 */
		return;
	}

	let data = file.data;
	if (isRefresh) {
		data = $.extend(true, [], file.data);
	}
	if (data.length == 0) {
		data = sheetmanage.buildGridData(file);
	}

	// luckysheetformula.updatecell(row, column, value);
	let formatList = {
		//ct:1, //celltype,Cell value format: text, time, etc.
		bg: 1, //background,#fff000
		ff: 1, //fontfamily,
		fc: 1, //fontcolor
		bl: 1, //Bold
		it: 1, //italic
		fs: 1, //font size
		cl: 1, //Cancelline, 0 Regular, 1 Cancelline
		un: 1, //underline, 0 Regular, 1 underlines, fonts
		vt: 1, //Vertical alignment, 0 middle, 1 up, 2 down
		ht: 1, //Horizontal alignment,0 center, 1 left, 2 right
		mc: 1, //Merge Cells
		tr: 1, //Text rotation,0: 0、1: 45 、2: -45、3 Vertical text、4: 90 、5: -90
		tb: 1, //Text wrap,0 truncation, 1 overflow, 2 word wrap
		//v: 1, //Original value
		//m: 1, //Display value
		rt: 1, //text rotation angle 0-180 alignment
		//f: 1, //formula
		qp: 1, //quotePrefix, show number as string
	};

	if (value == null || value.toString().length == 0) {
		formula.delFunctionGroup(row, column);
		setcellvalue(row, column, data, value);
	} else if (value instanceof Object) {
		let curv = {};
		if (isRealNull(data[row][column])) {
			data[row][column] = {};
		}
		let cell = data[row][column];
		if (value.f != null && value.v == null) {
			curv.f = value.f;
			if (value.ct != null) {
				curv.ct = value.ct;
			}
			data = luckysheetformula.updatecell(row, column, curv, false).data; //update formula value
		} else {
			if (value.ct != null) {
				curv.ct = value.ct;
			}
			if (value.f != null) {
				curv.f = value.f;
			}
			if (value.v != null) {
				curv.v = value.v;
			} else {
				curv.v = cell.v;
			}
			if (value.m != null) {
				curv.m = value.m;
			}
			formula.delFunctionGroup(row, column);
			setcellvalue(row, column, data, curv); //update text value
		}
		for (let attr in value) {
			let v = value[attr];
			if (attr in formatList) {
				menuButton.updateFormatCell(
					data,
					attr,
					v,
					row,
					row,
					column,
					column
				); //change range format
			} else {
				cell[attr] = v;
			}
		}
		data[row][column] = cell;
	} else {
		if (
			value.toString().substr(0, 1) == "=" ||
			value.toString().substr(0, 5) == "<span"
		) {
			data = luckysheetformula.updatecell(row, column, value, false).data; //update formula value or convert inline string html to object
		} else {
			formula.delFunctionGroup(row, column);
			setcellvalue(row, column, data, value);
		}
	}

	/* cell更新后触发  */
	setTimeout(() => {
		// Hook function
		method.createHookFunction(
			"cellUpdated",
			row,
			column,
			JSON.parse(oldValue),
			Store.flowdata[row][column],
			isRefresh
		);
	}, 0);

	if (file.index == Store.currentSheetIndex && isRefresh) {
		jfrefreshgrid(data, [{ row: [row, row], column: [column, column] }]); //update data, meanwhile refresh canvas and store data to history
	} else {
		file.data = data; //only update data
	}

	if (success && typeof success === "function") {
		success(data);
	}
}

/**
 * 清除指定工作表指定单元格的内容，返回清除掉的数据，不同于删除单元格的功能，不需要设定单元格移动情况
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function clearCell(row, column, options = {}) {
	if (!isRealNum(row) || !isRealNum(column)) {
		return tooltip.info(
			"Arguments row and column cannot be null or undefined.",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curSheetOrder, success } = { ...options };

	let targetSheetData = $.extend(true, [], Store.luckysheetfile[order].data);
	let cell = targetSheetData[row][column];

	if (getObjType(cell) == "object") {
		delete cell["m"];
		delete cell["v"];

		if (cell["f"] != null) {
			delete cell["f"];
			formula.delFunctionGroup(row, column, order);

			delete cell["spl"];
		}
	} else {
		cell = null;
	}

	// 若操作为当前sheet页，则刷新当前sheet页
	if (order === curSheetOrder) {
		jfrefreshgrid(targetSheetData, [
			{
				row: [row, row],
				column: [column, column],
			},
		]);
	} else {
		Store.luckysheetfile[order].data = targetSheetData;
	}

	if (success && typeof success === "function") {
		success(cell);
	}
}

/**
 * 删除指定工作表指定单元格，返回删除掉的数据，同时，指定是右侧单元格左移还是下方单元格上移
 * @param {String} move 删除后，右侧还是下方的单元格移动。可选值为 'left'、'up'
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteCell(move, row, column, options = {}) {
	let moveTypes = ["left", "up"];
	if (!move || moveTypes.indexOf(move) < 0) {
		return tooltip.info(
			"Arguments move cannot be null or undefined and its value must be 'left' or 'up'",
			""
		);
	}

	if (!isRealNum(row) || !isRealNum(column)) {
		return tooltip.info(
			"Arguments row and column cannot be null or undefined.",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curSheetOrder, success } = { ...options };

	let moveType = "move" + move.replace(move[0], move[0].toUpperCase()); // left-moveLeft;  up-moveUp

	let sheetIndex;
	if (order) {
		if (Store.luckysheetfile[order]) {
			sheetIndex = Store.luckysheetfile[order].index;
		}
	}

	luckysheetDeleteCell(moveType, row, row, column, column, sheetIndex);

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 设置某个单元格的属性，如果要设置单元格的值或者同时设置多个单元格属性，推荐使用setCellValue
 * @param {Number} row 单元格所在行数；从0开始的整数，0表示第一行
 * @param {Number} column 单元格所在列数；从0开始的整数，0表示第一列
 * @param {String} attr
 * @param {Number | String | Object} value 具体的设置值，一个属性会对应多个值，参考 单元格属性表的值示例，特殊情况：如果属性类型attr是单元格格式ct，则设置值value应提供ct.fa，比如设置A1单元格的格式为百分比格式：luckysheet.setCellFormat(0, 0, "ct", "0.00%")
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数, callback参数为改变后的cell对象
 */
export function setCellFormat(row, column, attr, value, options = {}) {
	if (!isRealNum(row) || !isRealNum(column)) {
		return tooltip.info(
			"Arguments row or column cannot be null or undefined.",
			""
		);
	}

	if (!attr) {
		return tooltip.info("Arguments attr cannot be null or undefined.", "");
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curSheetOrder, success } = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let targetSheetData = $.extend(true, [], file.data);
	if (targetSheetData.length == 0) {
		targetSheetData = sheetmanage.buildGridData(file);
	}

	let cellData = targetSheetData[row][column] || {};
	let cfg = $.extend(true, {}, file.config);

	// 特殊格式
	if (
		attr == "ct" &&
		(!value || !value.hasOwnProperty("fa") || !value.hasOwnProperty("t"))
	) {
		return new TypeError(
			"While set attribute 'ct' to cell, the value must have property 'fa' and 't'"
		);
	}

	if (attr == "bd") {
		if (cfg["borderInfo"] == null) {
			cfg["borderInfo"] = [];
		}

		let borderInfo = {
			rangeType: "range",
			borderType: "border-all",
			color: "#000",
			style: "1",
			range: [
				{
					column: [column, column],
					row: [row, row],
				},
			],
			...value,
		};

		cfg["borderInfo"].push(borderInfo);
	} else {
		cellData[attr] = value;
	}

	targetSheetData[row][column] = cellData;

	// refresh
	if (file.index == Store.currentSheetIndex) {
		file.config = cfg;
		Store.config = cfg;
		jfrefreshgrid(targetSheetData, [
			{ row: [row, row], column: [column, column] },
		]);
	} else {
		file.config = cfg;
		file.data = targetSheetData;
	}

	if (success && typeof success === "function") {
		success(cellData);
	}
}

/**
 * 查找一个工作表中的指定内容，返回查找到的内容组成的单元格一位数组，数据格式同celldata
 * @param {String} content 要查找的内容 可以为正则表达式（不包含前后'/')
 * @param {Object} options 可选参数
 * @param {Boolean} options.isRegularExpression 是否正则表达式匹配；默认为 false. 注意：正则中的规则需要转义，如\S需要写成 \\S
 * @param {Boolean} options.isWholeWord 是否整词匹配；默认为 false
 * @param {Boolean} options.isCaseSensitive 是否区分大小写匹配；默认为 false
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {String} options.type 单元格属性；默认值为m
 */
export function find(content, options = {}) {
	if (!content && content != 0) {
		return tooltip.info("Search content cannot be null or empty", "");
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let {
		isRegularExpression = false,
		isWholeWord = false,
		isCaseSensitive = false,
		order = curSheetOrder,
		type = "m",
	} = { ...options };
	let targetSheetData = Store.luckysheetfile[order].data;

	let result = [];
	for (let i = 0; i < targetSheetData.length; i++) {
		const rowArr = targetSheetData[i];

		for (let j = 0; j < rowArr.length; j++) {
			const cell = rowArr[j];

			if (!cell) {
				continue;
			}

			// 添加cell的row, column属性
			// replace方法中的setCellValue中需要使用该属性
			cell.row = i;
			cell.column = j;

			if (isWholeWord) {
				if (isCaseSensitive) {
					if (content.toString() == cell[type]) {
						result.push(cell);
					}
				} else {
					if (
						cell[type] &&
						content.toString().toLowerCase() ==
							cell[type].toLowerCase()
					) {
						result.push(cell);
					}
				}
			} else if (isRegularExpression) {
				let reg;
				if (isCaseSensitive) {
					reg = new RegExp(func_methods.getRegExpStr(content), "g");
				} else {
					reg = new RegExp(func_methods.getRegExpStr(content), "ig");
				}
				if (reg.test(cell[type])) {
					result.push(cell);
				}
			} else if (isCaseSensitive) {
				let reg = new RegExp(func_methods.getRegExpStr(content), "g");
				if (reg.test(cell[type])) {
					result.push(cell);
				}
			} else {
				let reg = new RegExp(func_methods.getRegExpStr(content), "ig");
				if (reg.test(cell[type])) {
					result.push(cell);
				}
			}
		}
	}

	return result;
}

/**
 * 查找一个工作表中的指定内容并替换成新的内容，返回替换后的内容组成的单元格一位数组，数据格式同celldata。
 * @param {String} content 要查找的内容
 * @param {String} replaceContent 要替换的内容
 * @param {Object} options 可选参数
 * @param {Boolean} options.isRegularExpression 是否正则表达式匹配；默认为 false
 * @param {Boolean} options.isWholeWord 是否整词匹配；默认为 false
 * @param {Boolean} options.isCaseSensitive 是否区分大小写匹配；默认为 false
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数, callback参数为替换后的cell集合
 */
export function replace(content, replaceContent, options = {}) {
	let matchCells = find(content, options);
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curSheetOrder } = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}
	let sheetData = $.extend(true, [], file.data);

	matchCells.forEach((cell) => {
		cell.m = replaceContent;
		setCellValue(cell.row, cell.column, replaceContent, {
			order: order,
			isRefresh: false,
		});
	});

	let fileData = $.extend(true, [], file.data);
	file.data.length = 0;
	file.data.push(...sheetData);

	if (file.index == Store.currentSheetIndex) {
		jfrefreshgrid(fileData, undefined, undefined, true, false);
	}

	luckysheetrefreshgrid();

	if (options.success && typeof options.success === "function") {
		options.success(matchCells);
	}
	return matchCells;
}

/**
 * 手动触发退出编辑模式
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function exitEditMode(options = {}) {
	if (parseInt($("#luckysheet-input-box").css("top")) > 0) {
		if (
			$("#luckysheet-formula-search-c").is(":visible") &&
			formula.searchFunctionCell != null
		) {
			formula.searchFunctionEnter(
				$("#luckysheet-formula-search-c").find(
					".luckysheet-formula-search-item-active"
				)
			);
		} else {
			formula.updatecell(
				Store.luckysheetCellUpdate[0],
				Store.luckysheetCellUpdate[1]
			);
			Store.luckysheet_select_save = [
				{
					row: [
						Store.luckysheetCellUpdate[0],
						Store.luckysheetCellUpdate[0],
					],
					column: [
						Store.luckysheetCellUpdate[1],
						Store.luckysheetCellUpdate[1],
					],
					row_focus: Store.luckysheetCellUpdate[0],
					column_focus: Store.luckysheetCellUpdate[1],
				},
			];
		}

		//若有参数弹出框，隐藏
		if ($("#luckysheet-search-formula-parm").is(":visible")) {
			$("#luckysheet-search-formula-parm").hide();
		}
		//若有参数选取范围弹出框，隐藏
		if ($("#luckysheet-search-formula-parm-select").is(":visible")) {
			$("#luckysheet-search-formula-parm-select").hide();
		}
	}

	if (options.success && typeof options.success === "function") {
		options.success();
	}
}

/**
 * 手动触发进入编辑模式
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function enterEditMode(options = {}) {
	if ($("#luckysheet-conditionformat-dialog").is(":visible")) {
		return;
	} else if ($("#luckysheet-cell-selected").is(":visible")) {
		let last =
			Store.luckysheet_select_save[
				Store.luckysheet_select_save.length - 1
			];

		let row_index = last["row_focus"],
			col_index = last["column_focus"];

		luckysheetupdateCell(row_index, col_index, Store.flowdata);
	}

	if (options.success && typeof options.success === "function") {
		options.success();
	}
}

/**
 * 冻结首行
 * 若设置冻结的sheet不是当前sheet页，只设置参数不渲染
 * @param {Number | String} order 工作表索引
 */
export function frozenFirstRow(order) {
	// store frozen
	luckysheetFreezen.saveFrozen("freezenRow", order);

	// 冻结为当前sheet页
	if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
		let freezenhorizontaldata, row_st, top;
		if (luckysheetFreezen.freezenRealFirstRowColumn) {
			let row_st = 0;
			top = Store.visibledatarow[row_st] - 2 + Store.columnHeaderHeight;
			freezenhorizontaldata = [
				Store.visibledatarow[row_st],
				row_st + 1,
				0,
				luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
				top,
			];
		} else {
			let scrollTop = $("#luckysheet-cell-main").scrollTop();
			row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);
			if (row_st == -1) {
				row_st = 0;
			}

			top =
				Store.visibledatarow[row_st] -
				2 -
				scrollTop +
				Store.columnHeaderHeight;
			freezenhorizontaldata = [
				Store.visibledatarow[row_st],
				row_st + 1,
				scrollTop,
				luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
				top,
			];
		}

		luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

		if (luckysheetFreezen.freezenverticaldata != null) {
			luckysheetFreezen.cancelFreezenVertical();
			luckysheetFreezen.createAssistCanvas();
			luckysheetrefreshgrid();
		}

		luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);
		luckysheetFreezen.createAssistCanvas();
		luckysheetrefreshgrid();
	}
}

/**
 * 冻结首列
 * 若设置冻结的sheet不是当前sheet页，只设置参数不渲染
 * @param {Number | String} order 工作表索引
 */
export function frozenFirstColumn(order) {
	// store frozen
	luckysheetFreezen.saveFrozen("freezenColumn", order);

	// 冻结为当前sheet页
	if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
		let freezenverticaldata, col_st, left;
		if (luckysheetFreezen.freezenRealFirstRowColumn) {
			col_st = 0;
			left = Store.visibledatacolumn[col_st] - 2 + Store.rowHeaderWidth;
			freezenverticaldata = [
				Store.visibledatacolumn[col_st],
				col_st + 1,
				0,
				luckysheetFreezen.cutVolumn(
					Store.visibledatacolumn,
					col_st + 1
				),
				left,
			];
		} else {
			let scrollLeft = $("#luckysheet-cell-main").scrollLeft();

			col_st = luckysheet_searcharray(
				Store.visibledatacolumn,
				scrollLeft
			);
			if (col_st == -1) {
				col_st = 0;
			}

			left =
				Store.visibledatacolumn[col_st] -
				2 -
				scrollLeft +
				Store.rowHeaderWidth;
			freezenverticaldata = [
				Store.visibledatacolumn[col_st],
				col_st + 1,
				scrollLeft,
				luckysheetFreezen.cutVolumn(
					Store.visibledatacolumn,
					col_st + 1
				),
				left,
			];
		}

		luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

		if (luckysheetFreezen.freezenhorizontaldata != null) {
			luckysheetFreezen.cancelFreezenHorizontal();
			luckysheetFreezen.createAssistCanvas();
			luckysheetrefreshgrid();
		}

		luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);
		luckysheetFreezen.createAssistCanvas();
		luckysheetrefreshgrid();
	}
}

/**
 * 冻结行选区
 * @param {Object} range 行选区范围的focus单元格的行列值构成的对象；格式为{ row_focus:0, column_focus:0 }
 * @param {Number | String} order 工作表索引
 */
export function frozenRowRange(range, order) {
	const locale_frozen = locale().freezen;

	if (
		!range ||
		(!range.hasOwnProperty("row_focus") && !formula.iscelldata(range))
	) {
		if (isEditMode()) {
			alert(locale_frozen.noSeletionError);
		} else {
			tooltip.info(locale_frozen.noSeletionError, "");
		}
		return;
	}

	if (typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
		range = {
			row_focus: range.row[0],
			column_focus: range.column[0],
		};
	}
	// store frozen
	luckysheetFreezen.saveFrozen("freezenRowRange", order, range);

	if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
		let scrollTop = $("#luckysheet-cell-main").scrollTop();
		let row_st = luckysheet_searcharray(Store.visibledatarow, scrollTop);

		let row_focus = range.row_focus;
		if (row_focus > row_st) {
			row_st = row_focus;
		}
		if (row_st == -1) {
			row_st = 0;
		}

		let top =
			Store.visibledatarow[row_st] -
			2 -
			scrollTop +
			Store.columnHeaderHeight;
		let freezenhorizontaldata = [
			Store.visibledatarow[row_st],
			row_st + 1,
			scrollTop,
			luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
			top,
		];
		luckysheetFreezen.saveFreezen(freezenhorizontaldata, top, null, null);

		if (luckysheetFreezen.freezenverticaldata != null) {
			luckysheetFreezen.cancelFreezenVertical();
			luckysheetFreezen.createAssistCanvas();
			luckysheetrefreshgrid();
		}

		luckysheetFreezen.createFreezenHorizontal(freezenhorizontaldata, top);
		luckysheetFreezen.createAssistCanvas();
		luckysheetrefreshgrid();
	}
}

/**
 * 冻结列选区
 * @param {Object} range 列选区范围的focus单元格的行列值构成的对象；格式为{ row_focus:0, column_focus:0 }
 * @param {Number | String} order 工作表索引
 */
export function frozenColumnRange(range, order) {
	const locale_frozen = locale().freezen;
	let isStringRange = typeof range === "string" && formula.iscelldata(range);

	if (!range || (!range.hasOwnProperty("column_focus") && !isStringRange)) {
		if (isEditMode()) {
			alert(locale_frozen.noSeletionError);
		} else {
			tooltip.info(locale_frozen.noSeletionError, "");
		}
		return;
	}

	if (isStringRange) {
		range = formula.getcellrange(range);
		range = {
			row_focus: range.row[0],
			column_focus: range.column[0],
		};
	}
	// store frozen
	luckysheetFreezen.saveFrozen("freezenColumnRange", order, range);

	if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
		let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
		let col_st = luckysheet_searcharray(
			Store.visibledatacolumn,
			scrollLeft
		);

		let column_focus = range.column_focus;
		if (column_focus > col_st) {
			col_st = column_focus;
		}
		if (col_st == -1) {
			col_st = 0;
		}

		let left =
			Store.visibledatacolumn[col_st] -
			2 -
			scrollLeft +
			Store.rowHeaderWidth;
		let freezenverticaldata = [
			Store.visibledatacolumn[col_st],
			col_st + 1,
			scrollLeft,
			luckysheetFreezen.cutVolumn(Store.visibledatacolumn, col_st + 1),
			left,
		];
		luckysheetFreezen.saveFreezen(null, null, freezenverticaldata, left);

		if (luckysheetFreezen.freezenhorizontaldata != null) {
			luckysheetFreezen.cancelFreezenHorizontal();
			luckysheetFreezen.createAssistCanvas();
			luckysheetrefreshgrid();
		}

		luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);
		luckysheetFreezen.createAssistCanvas();
		luckysheetrefreshgrid();
	}
}

/**
 * 取消冻结
 * @param {Number | String} order
 */
export function cancelFrozen(order) {
	luckysheetFreezen.saveFrozen("freezenCancel", order);

	// 取消当前sheet冻结时，刷新canvas
	if (!order || order == getSheetIndex(Store.currentSheetIndex)) {
		if (luckysheetFreezen.freezenverticaldata != null) {
			luckysheetFreezen.cancelFreezenVertical();
		}
		if (luckysheetFreezen.freezenhorizontaldata != null) {
			luckysheetFreezen.cancelFreezenHorizontal();
		}
		luckysheetFreezen.createAssistCanvas();
		luckysheetrefreshgrid();
	}
}

/**
 * 冻结行操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结行到选区 true-冻结行到选区  false-冻结首行
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setHorizontalFrozen(isRange, options = {}) {
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { range, order = curSheetOrder, success } = { ...options };

	// 若已存在冻结，取消之前的冻结效果
	cancelFrozen(order);

	if (!isRange) {
		frozenFirstRow(order);
	} else {
		// 选区行冻结
		frozenRowRange(range, order);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 冻结列操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结列到选区 true-冻结列到选区  false-冻结首列
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setVerticalFrozen(isRange, options = {}) {
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { range, order = curSheetOrder, success } = { ...options };

	// 若已存在冻结，取消之前的冻结效果
	cancelFrozen(order);

	if (!isRange) {
		frozenFirstColumn(order);
	} else {
		frozenColumnRange(range, order);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 冻结行列操作。特别注意，只有在isRange设置为true的时候，才需要设置setting中的range，且与一般的range格式不同。
 * @param {Boolean} isRange 是否冻结行列到选区 true-冻结行列到选区  false-冻结首行列
 * @param {Object} options 可选参数
 * @param {Object} options.range isRange为true的时候设置，开启冻结的单元格位置，格式为{ row_focus:0, column_focus:0 }，意为当前激活的单元格的行数和列数；默认从当前选区最后的一个选区中取得
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setBothFrozen(isRange, options = {}) {
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { range, order = curSheetOrder, success } = { ...options };

	let isCurrentSheet =
		!order || order == getSheetIndex(Store.currentSheetIndex);
	const locale_frozen = locale().freezen;

	// 若已存在冻结，取消之前的冻结效果
	cancelFrozen(order);

	// 冻结首行列
	if (!isRange) {
		// store frozen
		luckysheetFreezen.saveFrozen("freezenRC", order);

		if (isCurrentSheet) {
			let scrollTop = $("#luckysheet-cell-main").scrollTop();
			let row_st = luckysheet_searcharray(
				Store.visibledatarow,
				scrollTop
			);
			if (row_st == -1) {
				row_st = 0;
			}
			let top =
				Store.visibledatarow[row_st] -
				2 -
				scrollTop +
				Store.columnHeaderHeight;
			let freezenhorizontaldata = [
				Store.visibledatarow[row_st],
				row_st + 1,
				scrollTop,
				luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
				top,
			];
			luckysheetFreezen.saveFreezen(
				freezenhorizontaldata,
				top,
				null,
				null
			);

			luckysheetFreezen.createFreezenHorizontal(
				freezenhorizontaldata,
				top
			);

			let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
			let col_st = luckysheet_searcharray(
				Store.visibledatacolumn,
				scrollLeft
			);
			if (col_st == -1) {
				col_st = 0;
			}
			let left =
				Store.visibledatacolumn[col_st] -
				2 -
				scrollLeft +
				Store.rowHeaderWidth;
			let freezenverticaldata = [
				Store.visibledatacolumn[col_st],
				col_st + 1,
				scrollLeft,
				luckysheetFreezen.cutVolumn(
					Store.visibledatacolumn,
					col_st + 1
				),
				left,
			];
			luckysheetFreezen.saveFreezen(
				null,
				null,
				freezenverticaldata,
				left
			);

			luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);

			luckysheetFreezen.createAssistCanvas();
			luckysheetrefreshgrid();
		}
	} else {
		// 冻结行列到选区
		// store frozen
		luckysheetFreezen.saveFrozen("freezenRCRange", order, range);

		let isStringRange =
			typeof range === "string" && formula.iscelldata(range);
		if (isCurrentSheet) {
			if (
				(!range ||
					!(
						range.hasOwnProperty("column_focus") &&
						range.hasOwnProperty("row_focus")
					)) &&
				!isStringRange
			) {
				if (isEditMode()) {
					alert(locale_frozen.noSeletionError);
				} else {
					tooltip.info(locale_frozen.noSeletionError, "");
				}
				return;
			}

			if (isStringRange) {
				range = formula.getcellrange(range);
				range = {
					row_focus: range.row[0],
					column_focus: range.column[0],
				};
			}

			let scrollTop = $("#luckysheet-cell-main").scrollTop();
			let row_st = luckysheet_searcharray(
				Store.visibledatarow,
				scrollTop
			);

			let row_focus = range.row_focus;

			if (row_focus > row_st) {
				row_st = row_focus;
			}

			if (row_st == -1) {
				row_st = 0;
			}

			let top =
				Store.visibledatarow[row_st] -
				2 -
				scrollTop +
				Store.columnHeaderHeight;
			let freezenhorizontaldata = [
				Store.visibledatarow[row_st],
				row_st + 1,
				scrollTop,
				luckysheetFreezen.cutVolumn(Store.visibledatarow, row_st + 1),
				top,
			];
			luckysheetFreezen.saveFreezen(
				freezenhorizontaldata,
				top,
				null,
				null
			);

			luckysheetFreezen.createFreezenHorizontal(
				freezenhorizontaldata,
				top
			);

			let scrollLeft = $("#luckysheet-cell-main").scrollLeft();
			let col_st = luckysheet_searcharray(
				Store.visibledatacolumn,
				scrollLeft
			);

			let column_focus = range.column_focus;

			if (column_focus > col_st) {
				col_st = column_focus;
			}

			if (col_st == -1) {
				col_st = 0;
			}

			let left =
				Store.visibledatacolumn[col_st] -
				2 -
				scrollLeft +
				Store.rowHeaderWidth;
			let freezenverticaldata = [
				Store.visibledatacolumn[col_st],
				col_st + 1,
				scrollLeft,
				luckysheetFreezen.cutVolumn(
					Store.visibledatacolumn,
					col_st + 1
				),
				left,
			];
			luckysheetFreezen.saveFreezen(
				null,
				null,
				freezenverticaldata,
				left
			);

			luckysheetFreezen.createFreezenVertical(freezenverticaldata, left);

			luckysheetFreezen.createAssistCanvas();
			luckysheetrefreshgrid();
		}
	}
}

/**
 * 在第index行或列的位置，插入number行或列
 * @param {String} type 插入行或列 row-行  column-列
 * @param {Number} index 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowOrColumn(type, index = 0, options = {}) {
	if (!isRealNum(index)) {
		return tooltip.info("The index parameter is invalid.", "");
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { number = 1, order = curSheetOrder, success } = { ...options };

	let _locale = locale();
	let locale_info = _locale.info;
	if (!isRealNum(number)) {
		if (isEditMode()) {
			alert(locale_info.tipInputNumber);
		} else {
			tooltip.info(locale_info.tipInputNumber, "");
		}
		return;
	}

	number = parseInt(number);
	if (number < 1 || number > 100) {
		if (isEditMode()) {
			alert(locale_info.tipInputNumberLimit);
		} else {
			tooltip.info(locale_info.tipInputNumberLimit, "");
		}
		return;
	}

	// 默认在行上方增加行，列左侧增加列
	let sheetIndex;
	if (order) {
		if (Store.luckysheetfile[order]) {
			sheetIndex = Store.luckysheetfile[order].index;
		}
	}

	luckysheetextendtable(type, index, number, "lefttop", sheetIndex);

	if (success && typeof success === "function") {
		success();
	}
}
/**
 * 在第index行或列的位置，插入number行或列
 * @param {String} type 插入行或列 row-行  column-列
 * @param {Number} index 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowBottomOrColumnRight(type, index = 0, options = {}) {
	if (!isRealNum(index)) {
		return tooltip.info("The index parameter is invalid.", "");
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { number = 1, order = curSheetOrder, success } = { ...options };

	let _locale = locale();
	let locale_info = _locale.info;
	if (!isRealNum(number)) {
		if (isEditMode()) {
			alert(locale_info.tipInputNumber);
		} else {
			tooltip.info(locale_info.tipInputNumber, "");
		}
		return;
	}

	number = parseInt(number);
	if (number < 1 || number > 100) {
		if (isEditMode()) {
			alert(locale_info.tipInputNumberLimit);
		} else {
			tooltip.info(locale_info.tipInputNumberLimit, "");
		}
		return;
	}

	// 默认在行上方增加行，列左侧增加列
	let sheetIndex;
	if (order) {
		if (Store.luckysheetfile[order]) {
			sheetIndex = Store.luckysheetfile[order].index;
		}
	}

	luckysheetextendtable(type, index, number, "rightbottom", sheetIndex);

	if (success && typeof success === "function") {
		success();
	}
}
/**
 * 在第row行的位置，插入number行空白行
 * @param {Number} row 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRow(row = 0, options = {}) {
	insertRowOrColumn("row", row, options);
}
/**
 * 在第row行的位置，插入number行空白行
 * @param {Number} row 在第几行插入空白行，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白行数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertRowBottom(row = 0, options = {}) {
	insertRowBottomOrColumnRight("row", row, options);
}
/**
 * 在第column列的位置，插入number列空白列
 * @param {Number} column 在第几列插入空白列，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白列数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertColumn(column = 0, options = {}) {
	insertRowOrColumn("column", column, options);
}
/**
 * 在第column列的位置，插入number列空白列
 * @param {Number} column 在第几列插入空白列，从0开始
 * @param {Object} options 可选参数
 * @param {Number} options.number 插入的空白列数；默认为 1
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertColumnRight(column = 0, options = {}) {
	insertRowBottomOrColumnRight("column", column, options);
}
/**
 * 删除指定的行或列。删除行列之后，行列的序号并不会变化，下面的行（右侧的列）会补充到上（左）面，注意观察数据是否被正确删除即可。
 * @param {String} type 删除行或列 row-行  column-列
 * @param {Number} startIndex 要删除的起始行或列
 * @param {Number} endIndex 要删除的结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRowOrColumn(type, startIndex, endIndex, options = {}) {
	if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
		return tooltip.info(
			"Please enter the index for deleting rows or columns correctly.",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curSheetOrder, success } = { ...options };

	let sheetIndex;
	if (order) {
		if (Store.luckysheetfile[order]) {
			sheetIndex = Store.luckysheetfile[order].index;
		}
	}
	luckysheetdeletetable(type, startIndex, endIndex, sheetIndex);

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 删除指定的行。
 * @param {Number} rowStart 要删除的起始行
 * @param {Number} rowEnd 要删除的结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRow(rowStart, rowEnd, options = {}) {
	deleteRowOrColumn("row", rowStart, rowEnd, options);
}

/**
 * 删除指定的列。
 * @param {Number} columnStart 要删除的起始列
 * @param {Number} columnEnd 要删除的结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteColumn(columnStart, columnEnd, options = {}) {
	deleteRowOrColumn("column", columnStart, columnEnd, options);
}

/**
 * 隐藏行或列
 * @param {String} type 隐藏行或列  row-隐藏行  column-隐藏列
 * @param {Number} startIndex 起始行或列
 * @param {Number} endIndex 结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideRowOrColumn(type, startIndex, endIndex, options = {}) {
	if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
		return tooltip.info(
			"Please enter the index for deleting rows or columns correctly.",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curSheetOrder, saveParam = true, success } = { ...options };

	let file = Store.luckysheetfile[order];
	let cfgKey = type === "row" ? "rowhidden" : "colhidden";
	let cfg = $.extend(true, {}, file.config);
	if (cfg[cfgKey] == null) {
		cfg[cfgKey] = {};
	}

	for (let i = startIndex; i <= endIndex; i++) {
		cfg[cfgKey][i] = 0;
	}

	//保存撤销
	if (Store.clearjfundo) {
		let redo = {};
		redo["type"] = type === "row" ? "showHidRows" : "showHidCols";
		redo["sheetIndex"] = file.index;
		redo["config"] = $.extend(true, {}, file.config);
		redo["curconfig"] = cfg;

		Store.jfundo.length = 0;
		Store.jfredo.push(redo);
	}

	Store.luckysheetfile[order].config = cfg;

	if (saveParam) {
		server.saveParam("cg", file.index, cfg[cfgKey], { k: cfgKey });
	}

	// 若操作sheet为当前sheet页，行高、列宽 刷新
	if (order == curSheetOrder) {
		//config
		Store.config = cfg;
		jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 显示隐藏的行或列
 * @param {String} type 显示行或列  row-显示行  column-显示列
 * @param {Number} startIndex 起始行或列
 * @param {Number} endIndex 结束行或列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showRowOrColumn(type, startIndex, endIndex, options = {}) {
	if (!isRealNum(startIndex) || !isRealNum(endIndex)) {
		return tooltip.info(
			"Please enter the index for deleting rows or columns correctly.",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curSheetOrder, saveParam = true, success } = { ...options };

	let file = Store.luckysheetfile[order];
	let cfgKey = type === "row" ? "rowhidden" : "colhidden";
	let cfg = $.extend(true, {}, file.config);
	if (cfg[cfgKey] == null) {
		return;
	}

	for (let i = startIndex; i <= endIndex; i++) {
		delete cfg[cfgKey][i];
	}

	//保存撤销
	if (Store.clearjfundo) {
		let redo = {};
		redo["type"] = type === "row" ? "showHidRows" : "showHidCols";
		redo["sheetIndex"] = file.index;
		redo["config"] = $.extend(true, {}, file.config);
		redo["curconfig"] = cfg;

		Store.jfundo.length = 0;
		Store.jfredo.push(redo);
	}

	//config
	Store.luckysheetfile[order].config = Store.config;

	if (saveParam) {
		server.saveParam("cg", file.index, cfg[cfgKey], { k: cfgKey });
	}

	// 若操作sheet为当前sheet页，行高、列宽 刷新
	if (order === curSheetOrder) {
		Store.config = cfg;
		jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 隐藏行
 * @param {Number} startIndex 起始行
 * @param {Number} endIndex 结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideRow(startIndex, endIndex, options = {}) {
	hideRowOrColumn("row", startIndex, endIndex, options);
}

/**
 * 显示行
 * @param {Number} startIndex 起始行
 * @param {Number} endIndex 结束行
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showRow(startIndex, endIndex, options = {}) {
	showRowOrColumn("row", startIndex, endIndex, options);
}

/**
 * 隐藏列
 * @param {Number} startIndex 起始列
 * @param {Number} endIndex 结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideColumn(startIndex, endIndex, options = {}) {
	hideRowOrColumn("column", startIndex, endIndex, options);
}

/**
 * 显示列
 * @param {Number} startIndex 起始列
 * @param {Number} endIndex 结束列
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function showColumn(startIndex, endIndex, options = {}) {
	showRowOrColumn("column", startIndex, endIndex, options);
}

/**
 * 设置指定行的高度。优先级最高，高于默认行高和用户自定义行高。
 * @param {Object} rowInfo 行数和高度对应关系
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRowHeight(rowInfo, options = {}) {
	if (getObjType(rowInfo) != "object") {
		return tooltip.info("The rowInfo parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let cfg = $.extend(true, {}, file.config);
	if (cfg["rowlen"] == null) {
		cfg["rowlen"] = {};
	}

	for (let r in rowInfo) {
		if (parseInt(r) >= 0) {
			let len = rowInfo[r];

			if (len === "auto") {
				cfg["rowlen"][parseInt(r)] = len;
			} else {
				if (Number(len) >= 0) {
					cfg["rowlen"][parseInt(r)] = Number(len);
				}
			}
		}
	}

	file.config = cfg;

	server.saveParam("cg", file.index, cfg["rowlen"], { k: "rowlen" });

	if (file.index == Store.currentSheetIndex) {
		Store.config = cfg;
		jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 设置指定列的宽度
 * @param {Object} columnInfo 行数和高度对应关系
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setColumnWidth(columnInfo, options = {}) {
	if (getObjType(columnInfo) != "object") {
		return tooltip.info("The columnInfo parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let cfg = $.extend(true, {}, file.config);
	if (cfg["columnlen"] == null) {
		cfg["columnlen"] = {};
	}

	for (let c in columnInfo) {
		if (parseInt(c) >= 0) {
			let len = columnInfo[c];

			if (len === "auto") {
				cfg["columnlen"][parseInt(c)] = len;
			} else {
				if (Number(len) >= 0) {
					cfg["columnlen"][parseInt(c)] = Number(len);
				}
			}
		}
	}

	file.config = cfg;

	server.saveParam("cg", file.index, cfg["columnlen"], { k: "columnlen" });

	if (file.index == Store.currentSheetIndex) {
		Store.config = cfg;
		jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 获取指定工作表指定行的高度，得到行号和高度对应关系的对象
 * @param {Array} rowInfo 行号下标组成的数组；行号下标从0开始；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getRowHeight(rowInfo, options = {}) {
	if (getObjType(rowInfo) != "array" || rowInfo.length == 0) {
		return tooltip.info("The rowInfo parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let cfg = $.extend(true, {}, file.config);
	let rowlen = cfg["rowlen"] || {};

	let rowlenObj = {};

	rowInfo.forEach((item) => {
		if (parseInt(item) >= 0) {
			let size = rowlen[parseInt(item)] || Store.defaultrowlen;
			rowlenObj[parseInt(item)] = size;
		}
	});

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return rowlenObj;
}

/**
 * 获取指定工作表指定列的宽度，得到列号和宽度对应关系的对象
 * @param {Array} columnInfo 行号下标组成的数组；行号下标从0开始；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getColumnWidth(columnInfo, options = {}) {
	if (getObjType(columnInfo) != "array" || columnInfo.length == 0) {
		return tooltip.info("The columnInfo parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let cfg = $.extend(true, {}, file.config);
	let columnlen = cfg["columnlen"] || {};

	let columnlenObj = {};

	columnInfo.forEach((item) => {
		if (parseInt(item) >= 0) {
			let size = columnlen[parseInt(item)] || Store.defaultcollen;
			columnlenObj[parseInt(item)] = size;
		}
	});

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return columnlenObj;
}

/**
 * 获取工作表的默认行高
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getDefaultRowHeight(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	// *返回指定的工作表默认行高，如果未配置就返回全局的默认行高
	return Store.luckysheetfile[order].defaultRowHeight || Store.defaultrowlen;
}

/**
 * 获取工作表的默认列宽
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function getDefaultColWidth(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	// *返回指定的工作表默认列宽，如果未配置就返回全局的默认列宽
	return Store.luckysheetfile[order].defaultColWidth || Store.defaultcollen;
}

/**
 * 返回当前选区对象的数组，可能存在多个选区。
 * 每个选区的格式为row/column信息组成的对象{row:[0,1],column:[0,1]}
 * @returns {Array}
 */
export function getRange() {
	let rangeArr = JSON.parse(JSON.stringify(Store.luckysheet_select_save));

	let result = [];

	for (let i = 0; i < rangeArr.length; i++) {
		let rangeItem = rangeArr[i];
		let temp = {
			row: rangeItem.row,
			column: rangeItem.column,
		};
		result.push(temp);
	}

	return result;
}

/**
 * 返回表示指定区域内所有单元格位置的数组，区别getRange方法，该方法以cell单元格(而非某块连续的区域)为单位来组织选区的数据
 * @param   {Array}   range 可选参数，默认为当前选中区域
 * @returns {Array}   对象数组
 */
export function getRangeWithFlatten(range) {
	range = range || getRange();

	let result = [];

	range.forEach((ele) => {
		// 这个data可能是个范围或者是单个cell
		let rs = ele.row;
		let cs = ele.column;
		for (let r = rs[0]; r <= rs[1]; r++) {
			for (let c = cs[0]; c <= cs[1]; c++) {
				// r c 当前的r和当前的c
				result.push({ r, c });
			}
		}
	});
	return result;
}

/**
 * 返回表示指定区域内所有单元格内容的对象数组
 * @param   {Array}   range 可选参数，默认为当前选中区域扁平化后的对象，结构形如[{r:0,c:0},{r:0,c:1}...]
 * @returns {Array}   对象数组
 */
export function getRangeValuesWithFlatte(range) {
	range = range || getRangeWithFlatten();

	let values = [];

	// 获取到的这个数据不是最新的数据
	range.forEach((item) => {
		values.push(Store.flowdata[item.r][item.c]);
	});
	return values;
}

/**
 * 返回对应当前选区的坐标字符串数组，可能存在多个选区。
 * 每个选区可能是单个单元格(如 A1)或多个单元格组成的矩形区域(如 D9:E12)
 * @returns {Array}
 */
export function getRangeAxis() {
	let result = [];
	let rangeArr = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
	let sheetIndex = Store.currentSheetIndex;

	rangeArr.forEach((ele) => {
		let axisText = getRangetxt(sheetIndex, {
			column: ele.column,
			row: ele.row,
		});
		result.push(axisText);
	});

	return result;
}

/**
 * 返回指定工作表指定范围的单元格二维数组数据，每个单元格为一个对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeValue(options = {}) {
	let curOrder = getSheetIndex(Store.currentSheetIndex);
	let { range, order = curOrder } = { ...options };

	let file = Store.luckysheetfile[order];

	if (!range || typeof range === "object") {
		return getdatabyselection(range, file.index);
	} else if (typeof range === "string") {
		if (formula.iscelldata(range)) {
			return getdatabyselection(formula.getcellrange(range), file.index);
		} else {
			tooltip.info(
				"The range is invalid, please check range parameter.",
				""
			);
		}
	}
}

/**
 * 复制指定工作表指定单元格区域的数据，返回包含`<table>`html格式的数据，可用于粘贴到excel中保持单元格样式。
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围
 * @param {order} options.order 工作表下标
 */
export function getRangeHtml(options = {}) {
	let {
		range = Store.luckysheet_select_save,
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };
	range = JSON.parse(JSON.stringify(range));

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = [
			{
				row: cellrange.row,
				column: cellrange.column,
			},
		];
	} else if (getObjType(range) == "object") {
		if (range.row == null || range.column == null) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		range = [
			{
				row: range.row,
				column: range.column,
			},
		];
	}

	if (getObjType(range) != "array") {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	//复制范围内包含部分合并单元格，提示
	let cfg = $.extend(true, {}, file.config);
	if (cfg["merge"] != null) {
		let has_PartMC = false;

		for (let s = 0; s < range.length; s++) {
			let r1 = range[s].row[0],
				r2 = range[s].row[1];
			let c1 = range[s].column[0],
				c2 = range[s].column[1];

			has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

			if (has_PartMC) {
				break;
			}
		}

		if (has_PartMC) {
			return tooltip.info(
				"Cannot perform this operation on partially merged cells",
				""
			);
		}
	}

	//多重选区 有条件格式时 提示
	let cdformat = $.extend(true, [], file.luckysheet_conditionformat_save);
	if (range.length > 1 && cdformat.length > 0) {
		let hasCF = false;
		let cf_compute = conditionformat.getComputeMap(file.index);

		for (let s = 0; s < range.length; s++) {
			let r1 = range[s].row[0],
				r2 = range[s].row[1];
			let c1 = range[s].column[0],
				c2 = range[s].column[1];

			for (let r = r1; r <= r2; r++) {
				for (let c = c1; c <= c2; c++) {
					if (conditionformat.checksCF(r, c, cf_compute) != null) {
						hasCF = true;
						break;
					}
				}

				if (hasCF) {
					break;
				}
			}

			if (hasCF) {
				break;
			}
		}

		if (hasCF) {
			return tooltip.info(
				"Cannot perform this operation on multiple selection areas, please select a single area",
				""
			);
		}
	}

	//多重选区 行不一样且列不一样时 提示
	if (range.length > 1) {
		let isSameRow = true,
			str_r = range[0].row[0],
			end_r = range[0].row[1];
		let isSameCol = true,
			str_c = range[0].column[0],
			end_c = range[0].column[1];

		for (let s = 1; s < range.length; s++) {
			if (range[s].row[0] != str_r || range[s].row[1] != end_r) {
				isSameRow = false;
			}

			if (range[s].column[0] != str_c || range[s].column[1] != end_c) {
				isSameCol = false;
			}
		}

		if ((!isSameRow && !isSameCol) || selectIsOverlap(range)) {
			return tooltip.info(
				"Cannot perform this operation on multiple selection areas, please select a single area",
				""
			);
		}
	}

	let rowIndexArr = [],
		colIndexArr = [];

	for (let s = 0; s < range.length; s++) {
		let r1 = range[s].row[0],
			r2 = range[s].row[1];
		let c1 = range[s].column[0],
			c2 = range[s].column[1];

		for (let r = r1; r <= r2; r++) {
			if (cfg["rowhidden"] != null && cfg["rowhidden"][r] != null) {
				continue;
			}

			if (!rowIndexArr.includes(r)) {
				rowIndexArr.push(r);
			}

			for (let c = c1; c <= c2; c++) {
				if (cfg["colhidden"] != null && cfg["colhidden"][c] != null) {
					continue;
				}

				if (!colIndexArr.includes(c)) {
					colIndexArr.push(c);
				}
			}
		}
	}

	let borderInfoCompute;
	if (cfg["borderInfo"] && cfg["borderInfo"].length > 0) {
		//边框
		borderInfoCompute = getBorderInfoCompute(file.index);
	}

	let d = file.data;
	if (d == null || d.length == 0) {
		d = sheetmanage.buildGridData(file);
	}

	let cpdata = "";
	let colgroup = "";

	rowIndexArr = rowIndexArr.sort((a, b) => a - b);
	colIndexArr = colIndexArr.sort((a, b) => a - b);

	for (let i = 0; i < rowIndexArr.length; i++) {
		let r = rowIndexArr[i];

		if (cfg["rowhidden"] != null && cfg["rowhidden"][r] != null) {
			continue;
		}

		cpdata += "<tr>";

		for (let j = 0; j < colIndexArr.length; j++) {
			let c = colIndexArr[j];

			if (cfg["colhidden"] != null && cfg["colhidden"][c] != null) {
				continue;
			}

			let column = '<td ${span} style="${style}">';

			if (d[r] != null && d[r][c] != null) {
				let style = "",
					span = "";

				if (r == rowIndexArr[0]) {
					if (
						cfg["columnlen"] == null ||
						cfg["columnlen"][c.toString()] == null
					) {
						colgroup += '<colgroup width="72px"></colgroup>';
					} else {
						colgroup +=
							'<colgroup width="' +
							cfg["columnlen"][c.toString()] +
							'px"></colgroup>';
					}
				}

				if (c == colIndexArr[0]) {
					if (
						cfg["rowlen"] == null ||
						cfg["rowlen"][r.toString()] == null
					) {
						style += "height:19px;";
					} else {
						style +=
							"height:" + cfg["rowlen"][r.toString()] + "px;";
					}
				}

				let reg = /^(w|W)((0?)|(0\.0+))$/;
				let c_value;
				if (
					d[r][c].ct != null &&
					d[r][c].ct.fa != null &&
					d[r][c].ct.fa.match(reg)
				) {
					c_value = getcellvalue(r, c, d);
				} else {
					c_value = getcellvalue(r, c, d, "m");
				}

				style += menuButton.getStyleByCell(d, r, c);

				if (getObjType(d[r][c]) == "object" && "mc" in d[r][c]) {
					if ("rs" in d[r][c]["mc"]) {
						span =
							'rowspan="' +
							d[r][c]["mc"].rs +
							'" colspan="' +
							d[r][c]["mc"].cs +
							'"';

						//边框
						if (
							borderInfoCompute &&
							borderInfoCompute[r + "_" + c]
						) {
							let bl_obj = { color: {}, style: {} },
								br_obj = { color: {}, style: {} },
								bt_obj = { color: {}, style: {} },
								bb_obj = { color: {}, style: {} };

							for (
								let bd_r = r;
								bd_r < r + d[r][c]["mc"].rs;
								bd_r++
							) {
								for (
									let bd_c = c;
									bd_c < c + d[r][c]["mc"].cs;
									bd_c++
								) {
									if (
										bd_r == r &&
										borderInfoCompute[bd_r + "_" + bd_c] &&
										borderInfoCompute[bd_r + "_" + bd_c].t
									) {
										let linetype =
											borderInfoCompute[bd_r + "_" + bd_c]
												.t.style;
										let bcolor =
											borderInfoCompute[bd_r + "_" + bd_c]
												.t.color;

										if (bt_obj["style"][linetype] == null) {
											bt_obj["style"][linetype] = 1;
										} else {
											bt_obj["style"][linetype] =
												bt_obj["style"][linetype] + 1;
										}

										if (bt_obj["color"][bcolor] == null) {
											bt_obj["color"][bcolor] = 1;
										} else {
											bt_obj["color"][bcolor] =
												bt_obj["color"][bcolor] + 1;
										}
									}

									if (
										bd_r == r + d[r][c]["mc"].rs - 1 &&
										borderInfoCompute[bd_r + "_" + bd_c] &&
										borderInfoCompute[bd_r + "_" + bd_c].b
									) {
										let linetype =
											borderInfoCompute[bd_r + "_" + bd_c]
												.b.style;
										let bcolor =
											borderInfoCompute[bd_r + "_" + bd_c]
												.b.color;

										if (bb_obj["style"][linetype] == null) {
											bb_obj["style"][linetype] = 1;
										} else {
											bb_obj["style"][linetype] =
												bb_obj["style"][linetype] + 1;
										}

										if (bb_obj["color"][bcolor] == null) {
											bb_obj["color"][bcolor] = 1;
										} else {
											bb_obj["color"][bcolor] =
												bb_obj["color"][bcolor] + 1;
										}
									}

									if (
										bd_c == c &&
										borderInfoCompute[bd_r + "_" + bd_c] &&
										borderInfoCompute[bd_r + "_" + bd_c].l
									) {
										let linetype =
											borderInfoCompute[r + "_" + c].l
												.style;
										let bcolor =
											borderInfoCompute[bd_r + "_" + bd_c]
												.l.color;

										if (bl_obj["style"][linetype] == null) {
											bl_obj["style"][linetype] = 1;
										} else {
											bl_obj["style"][linetype] =
												bl_obj["style"][linetype] + 1;
										}

										if (bl_obj["color"][bcolor] == null) {
											bl_obj["color"][bcolor] = 1;
										} else {
											bl_obj["color"][bcolor] =
												bl_obj["color"][bcolor] + 1;
										}
									}

									if (
										bd_c == c + d[r][c]["mc"].cs - 1 &&
										borderInfoCompute[bd_r + "_" + bd_c] &&
										borderInfoCompute[bd_r + "_" + bd_c].r
									) {
										let linetype =
											borderInfoCompute[bd_r + "_" + bd_c]
												.r.style;
										let bcolor =
											borderInfoCompute[bd_r + "_" + bd_c]
												.r.color;

										if (br_obj["style"][linetype] == null) {
											br_obj["style"][linetype] = 1;
										} else {
											br_obj["style"][linetype] =
												br_obj["style"][linetype] + 1;
										}

										if (br_obj["color"][bcolor] == null) {
											br_obj["color"][bcolor] = 1;
										} else {
											br_obj["color"][bcolor] =
												br_obj["color"][bcolor] + 1;
										}
									}
								}
							}

							let rowlen = d[r][c]["mc"].rs,
								collen = d[r][c]["mc"].cs;

							if (JSON.stringify(bl_obj).length > 23) {
								let bl_color = null,
									bl_style = null;

								for (let x in bl_obj.color) {
									if (bl_obj.color[x] >= rowlen / 2) {
										bl_color = x;
									}
								}

								for (let x in bl_obj.style) {
									if (bl_obj.style[x] >= rowlen / 2) {
										bl_style = x;
									}
								}

								if (bl_color != null && bl_style != null) {
									style +=
										"border-left:" +
										selection.getHtmlBorderStyle(
											bl_style,
											bl_color
										);
								}
							}

							if (JSON.stringify(br_obj).length > 23) {
								let br_color = null,
									br_style = null;

								for (let x in br_obj.color) {
									if (br_obj.color[x] >= rowlen / 2) {
										br_color = x;
									}
								}

								for (let x in br_obj.style) {
									if (br_obj.style[x] >= rowlen / 2) {
										br_style = x;
									}
								}

								if (br_color != null && br_style != null) {
									style +=
										"border-right:" +
										selection.getHtmlBorderStyle(
											br_style,
											br_color
										);
								}
							}

							if (JSON.stringify(bt_obj).length > 23) {
								let bt_color = null,
									bt_style = null;

								for (let x in bt_obj.color) {
									if (bt_obj.color[x] >= collen / 2) {
										bt_color = x;
									}
								}

								for (let x in bt_obj.style) {
									if (bt_obj.style[x] >= collen / 2) {
										bt_style = x;
									}
								}

								if (bt_color != null && bt_style != null) {
									style +=
										"border-top:" +
										selection.getHtmlBorderStyle(
											bt_style,
											bt_color
										);
								}
							}

							if (JSON.stringify(bb_obj).length > 23) {
								let bb_color = null,
									bb_style = null;

								for (let x in bb_obj.color) {
									if (bb_obj.color[x] >= collen / 2) {
										bb_color = x;
									}
								}

								for (let x in bb_obj.style) {
									if (bb_obj.style[x] >= collen / 2) {
										bb_style = x;
									}
								}

								if (bb_color != null && bb_style != null) {
									style +=
										"border-bottom:" +
										selection.getHtmlBorderStyle(
											bb_style,
											bb_color
										);
								}
							}
						}
					} else {
						continue;
					}
				} else {
					//边框
					if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
						//左边框
						if (borderInfoCompute[r + "_" + c].l) {
							let linetype =
								borderInfoCompute[r + "_" + c].l.style;
							let bcolor = borderInfoCompute[r + "_" + c].l.color;
							style +=
								"border-left:" +
								selection.getHtmlBorderStyle(linetype, bcolor);
						}

						//右边框
						if (borderInfoCompute[r + "_" + c].r) {
							let linetype =
								borderInfoCompute[r + "_" + c].r.style;
							let bcolor = borderInfoCompute[r + "_" + c].r.color;
							style +=
								"border-right:" +
								selection.getHtmlBorderStyle(linetype, bcolor);
						}

						//下边框
						if (borderInfoCompute[r + "_" + c].b) {
							let linetype =
								borderInfoCompute[r + "_" + c].b.style;
							let bcolor = borderInfoCompute[r + "_" + c].b.color;
							style +=
								"border-bottom:" +
								selection.getHtmlBorderStyle(linetype, bcolor);
						}

						//上边框
						if (borderInfoCompute[r + "_" + c].t) {
							let linetype =
								borderInfoCompute[r + "_" + c].t.style;
							let bcolor = borderInfoCompute[r + "_" + c].t.color;
							style +=
								"border-top:" +
								selection.getHtmlBorderStyle(linetype, bcolor);
						}
					}
				}

				column = replaceHtml(column, { style: style, span: span });

				if (c_value == null) {
					c_value = getcellvalue(r, c, d);
				}

				if (c_value == null) {
					c_value = " ";
				}

				column += c_value;
			} else {
				let style = "";

				//边框
				if (borderInfoCompute && borderInfoCompute[r + "_" + c]) {
					//左边框
					if (borderInfoCompute[r + "_" + c].l) {
						let linetype = borderInfoCompute[r + "_" + c].l.style;
						let bcolor = borderInfoCompute[r + "_" + c].l.color;
						style +=
							"border-left:" +
							selection.getHtmlBorderStyle(linetype, bcolor);
					}

					//右边框
					if (borderInfoCompute[r + "_" + c].r) {
						let linetype = borderInfoCompute[r + "_" + c].r.style;
						let bcolor = borderInfoCompute[r + "_" + c].r.color;
						style +=
							"border-right:" +
							selection.getHtmlBorderStyle(linetype, bcolor);
					}

					//下边框
					if (borderInfoCompute[r + "_" + c].b) {
						let linetype = borderInfoCompute[r + "_" + c].b.style;
						let bcolor = borderInfoCompute[r + "_" + c].b.color;
						style +=
							"border-bottom:" +
							selection.getHtmlBorderStyle(linetype, bcolor);
					}

					//上边框
					if (borderInfoCompute[r + "_" + c].t) {
						let linetype = borderInfoCompute[r + "_" + c].t.style;
						let bcolor = borderInfoCompute[r + "_" + c].t.color;
						style +=
							"border-top:" +
							selection.getHtmlBorderStyle(linetype, bcolor);
					}
				}

				column += "";

				if (r == rowIndexArr[0]) {
					if (
						cfg["columnlen"] == null ||
						cfg["columnlen"][c.toString()] == null
					) {
						colgroup += '<colgroup width="72px"></colgroup>';
					} else {
						colgroup +=
							'<colgroup width="' +
							cfg["columnlen"][c.toString()] +
							'px"></colgroup>';
					}
				}

				if (c == colIndexArr[0]) {
					if (
						cfg["rowlen"] == null ||
						cfg["rowlen"][r.toString()] == null
					) {
						style += "height:19px;";
					} else {
						style +=
							"height:" + cfg["rowlen"][r.toString()] + "px;";
					}
				}

				column = replaceHtml(column, { style: style, span: "" });
				column += " ";
			}

			column += "</td>";
			cpdata += column;
		}

		cpdata += "</tr>";
	}

	cpdata =
		'<table data-type="luckysheet_copy_action_table">' +
		colgroup +
		cpdata +
		"</table>";

	return cpdata;
}

/**
 * 复制指定工作表指定单元格区域的数据，返回一维、二维或者自定义行列数的二维数组的数据。只有在dimensional设置为custom的时候，才需要设置setting中的row和column
 * @param {String} dimensional 数组维度。可选值为：oneDimensional-一维数组；twoDimensional-二维数组； custom-自定义行列数的二维数组
 * @param {Object} options 可选参数
 * @param {Number} options.row dimensional为custom的时候设置，多维数组的行数
 * @param {Number} options.column dimensional为custom的时候设置，多维数组的列数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeArray(dimensional, options = {}) {
	let dimensionalValues = ["oneDimensional", "twoDimensional"];

	if (!dimensionalValues.includes(dimensional)) {
		return tooltip.info("The dimensional parameter is invalid.", "");
	}

	let {
		range = Store.luckysheet_select_save[
			Store.luckysheet_select_save.length - 1
		],
		order = getSheetIndex(Store.currentSheetIndex),
	} = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		range = formula.getcellrange(range);
	}

	if (
		getObjType(range) != "object" ||
		range.row == null ||
		range.column == null
	) {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let r1 = range.row[0],
		r2 = range.row[1];
	let c1 = range.column[0],
		c2 = range.column[1];

	//复制范围内包含部分合并单元格，提示
	let cfg = $.extend(true, {}, file.config);
	if (cfg["merge"] != null) {
		let has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

		if (has_PartMC) {
			return tooltip.info(
				"Cannot perform this operation on partially merged cells",
				""
			);
		}
	}

	let data = file.data;
	if (data == null || data.length == 0) {
		data = sheetmanage.buildGridData(file);
	}

	let dataArr = [];

	if (dimensional == "oneDimensional") {
		//一维数组
		for (let r = r1; r <= r2; r++) {
			for (let c = c1; c <= c2; c++) {
				let cell = data[r][c];

				if (cell == null || cell.v == null) {
					dataArr.push(null);
				} else {
					dataArr.push(cell.v);
				}
			}
		}
	} else if (dimensional == "twoDimensional") {
		for (let r = r1; r <= r2; r++) {
			let row = [];

			for (let c = c1; c <= c2; c++) {
				let cell = data[r][c];

				if (cell == null || cell.v == null) {
					row.push(null);
				} else {
					row.push(cell.v);
				}
			}

			dataArr.push(row);
		}
	}

	return dataArr;
}

/**
 * 复制指定工作表指定单元格区域的数据，返回json格式的数据
 * @param {Boolean} isFirstRowTitle 是否首行为标题
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeJson(isFirstRowTitle, options = {}) {
	let curRange = Store.luckysheet_select_save[0];
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { range = curRange, order = curSheetOrder } = { ...options };
	let file = Store.luckysheetfile[order];
	let config = file.config;

	if (range && typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	if (!range || range.length > 1) {
		if (isEditMode()) {
			alert(locale_drag.noMulti);
		} else {
			tooltip.info(locale_drag.noMulti, "");
		}
		return;
	}

	//复制范围内包含部分合并单元格，提示
	if (config["merge"] != null) {
		let has_PartMC = false;
		let r1 = range.row[0],
			r2 = range.row[1],
			c1 = range.column[0],
			c2 = range.column[1];
		has_PartMC = hasPartMC(config, r1, r2, c1, c2);

		if (has_PartMC) {
			if (isEditMode()) {
				alert(locale().drag.noPartMerge);
			} else {
				tooltip.info(locale().drag.noPartMerge, "");
			}
			return;
		}
	}
	let getdata = getdatabyselection(range, file.index);
	let arr = [];
	if (getdata.length === 0) {
		return;
	}
	if (isFirstRowTitle) {
		if (getdata.length === 1) {
			let obj = {};
			for (let i = 0; i < getdata[0].length; i++) {
				obj[getcellvalue(0, i, getdata)] = "";
			}
			arr.push(obj);
		} else {
			for (let r = 1; r < getdata.length; r++) {
				let obj = {};
				for (let c = 0; c < getdata[0].length; c++) {
					if (getcellvalue(0, c, getdata) == undefined) {
						obj[""] = getcellvalue(r, c, getdata);
					} else {
						obj[getcellvalue(0, c, getdata)] = getcellvalue(
							r,
							c,
							getdata
						);
					}
				}
				arr.push(obj);
			}
		}
	} else {
		let st = range["column"][0];
		for (let r = 0; r < getdata.length; r++) {
			let obj = {};
			for (let c = 0; c < getdata[0].length; c++) {
				obj[chatatABC(c + st)] = getcellvalue(r, c, getdata);
			}
			arr.push(obj);
		}
	}
	// selection.copybyformat(new Event('click'), JSON.stringify(arr));
	return arr;
}

/**
 *
 * @param {String} type 对角线还是对角线偏移 "normal"-对角线  "anti"-反对角线
"offset"-对角线偏移
 * @param {Object} options 可选参数
 * @param {Number} options.column type为offset的时候设置，对角偏移的列数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeDiagonal(type, options = {}) {
	let typeValues = ["normal", "anti", "offset"];
	if (typeValues.indexOf(type) < 0) {
		return tooltip.info(
			"The type parameter must be included in ['normal', 'anti', 'offset']",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
	let {
		column = 1,
		range = curRange,
		order = curSheetOrder,
	} = { ...options };

	let file = Store.luckysheetfile[order];
	let config = file.config;

	if (range && typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	if (!range || range.length > 1) {
		if (isEditMode()) {
			alert(locale().drag.noMulti);
		} else {
			tooltip.info(locale().drag.noMulti, "");
		}
		return;
	}

	//复制范围内包含部分合并单元格，提示
	if (config["merge"] != null) {
		let has_PartMC = false;
		let r1 = range[0].row[0],
			r2 = range[0].row[1],
			c1 = range[0].column[0],
			c2 = range[0].column[1];
		has_PartMC = hasPartMC(config, r1, r2, c1, c2);

		if (has_PartMC) {
			if (isEditMode()) {
				alert(locale().drag.noPartMerge);
			} else {
				tooltip.info(locale().drag.noPartMerge, "");
			}
			return;
		}
	}
	let getdata = getdatabyselection(range, order);
	let arr = [];
	if (getdata.length === 0) {
		return;
	}

	let clen = getdata[0].length;
	switch (type) {
		case "normal":
			for (let r = 0; r < getdata.length; r++) {
				if (r >= clen) {
					break;
				}
				arr.push(getdata[r][r]);
			}
			break;
		case "anti":
			for (let r = 0; r < getdata.length; r++) {
				if (r >= clen) {
					break;
				}
				arr.push(getdata[r][clen - r - 1]);
			}
			break;
		case "offset":
			if (column.toString() == "NaN") {
				if (isEditMode()) {
					alert(locale().drag.inputCorrect);
				} else {
					tooltip.info(locale().drag.inputCorrect, "");
				}
				return;
			}

			if (column < 0) {
				if (isEditMode()) {
					alert(locale().drag.offsetColumnLessZero);
				} else {
					tooltip.info(locale().drag.offsetColumnLessZero, "");
				}
				return;
			}

			for (let r = 0; r < getdata.length; r++) {
				if (r + column >= clen) {
					break;
				}
				arr.push(getdata[r][r + column]);
			}
			break;
	}
	selection.copybyformat(new Event(), JSON.stringify(arr));
}

/**
 * 复制指定工作表指定单元格区域的数据，返回布尔值的数据
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function getRangeBoolean(options = {}) {
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
	let { range = curRange, order = curSheetOrder } = { ...options };

	let file = Store.luckysheetfile[order];
	let config = file.config;

	if (range && typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	if (!range || range.length > 1) {
		if (isEditMode()) {
			alert(locale().drag.noMulti);
		} else {
			tooltip.info(locale().drag.noMulti, "");
		}
		return;
	}

	//复制范围内包含部分合并单元格，提示
	if (config["merge"] != null) {
		let has_PartMC = false;
		let r1 = range[0].row[0],
			r2 = range[0].row[1],
			c1 = range[0].column[0],
			c2 = range[0].column[1];
		has_PartMC = hasPartMC(config, r1, r2, c1, c2);

		if (has_PartMC) {
			if (isEditMode()) {
				alert(locale().drag.noPartMerge);
			} else {
				tooltip.info(locale().drag.noPartMerge, "");
			}
			return;
		}
	}
	let getdata = getdatabyselection(range, order);
	let arr = [];
	if (getdata.length === 0) {
		return;
	}
	for (let r = 0; r < getdata.length; r++) {
		let a = [];
		for (let c = 0; c < getdata[0].length; c++) {
			let bool = false;

			let v;
			if (getObjType(getdata[r][c]) == "object") {
				v = getdata[r][c].v;
			} else {
				v = getdata[r][c];
			}

			if (v == null || v == "") {
				bool = false;
			} else {
				v = parseInt(v);
				if (v == null || v > 0) {
					bool = true;
				} else {
					bool = false;
				}
			}
			a.push(bool);
		}
		arr.push(a);
	}

	selection.copybyformat(event, JSON.stringify(arr));
}

/**
 * 指定工作表选中一个或多个选区为选中状态并选择是否高亮，支持多种格式设置。
 * @param {Array | Object | String} range 选区范围
 * @param {Object} options 可选参数
 * @param {Boolean} options.show 是否显示高亮选中效果；默认值为 `true`
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeShow(range, options = {}) {
	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = [
			{
				row: cellrange.row,
				column: cellrange.column,
			},
		];
	} else if (getObjType(range) == "object") {
		if (range.row == null || range.column == null) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		range = [
			{
				row: range.row,
				column: range.column,
			},
		];
	}

	if (getObjType(range) == "array") {
		for (let i = 0; i < range.length; i++) {
			if (getObjType(range[i]) === "string") {
				if (!formula.iscelldata(range[i])) {
					return tooltip.info("The range parameter is invalid.", "");
				}
				let cellrange = formula.getcellrange(range[i]);
				range[i] = {
					row: cellrange.row,
					column: cellrange.column,
				};
			} else if (getObjType(range) == "object") {
				if (range.row == null || range.column == null) {
					return tooltip.info("The range parameter is invalid.", "");
				}
				range = {
					row: range.row,
					column: range.column,
				};
			}
		}
	}

	if (getObjType(range) != "array") {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let {
		show = true,
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	for (let i = 0; i < range.length; i++) {
		let changeparam = menuButton.mergeMoveMain(
			range[i].column,
			range[i].row,
			range[i]
		);
		if (changeparam) {
			range[i] = {
				row: changeparam[1],
				column: changeparam[0],
			};
		}
	}

	file.luckysheet_select_save = range;

	if (file.index == Store.currentSheetIndex) {
		Store.luckysheet_select_save = range;
		selectHightlightShow();

		if (!show) {
			$("#luckysheet-cell-selected-boxs").hide();
			$("#luckysheet-cell-selected-focus").hide();
			$("#luckysheet-row-count-show").hide();
			$("#luckysheet-column-count-show").hide();
			$("#luckysheet-rows-h-selected").empty();
			$("#luckysheet-cols-h-selected").empty();
		}
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 将一个单元格数组数据赋值到指定的区域，数据格式同getRangeValue方法取到的数据。
 * @param {Array[Array]} data 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Boolean} options.isRefresh 是否刷新界面；默认为true
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeValue(data, options = {}) {
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let curRange =
		Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];
	let {
		range = curRange,
		isRefresh = true,
		order = curSheetOrder,
		success,
	} = { ...options };

	if (data == null) {
		return tooltip.info(
			"The data which will be set to range cannot be null.",
			""
		);
	}

	if (range instanceof Array) {
		return tooltip.info(
			"setRangeValue only supports a single selection.",
			""
		);
	}

	if (typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	let rowCount = range.row[1] - range.row[0] + 1,
		columnCount = range.column[1] - range.column[0] + 1;

	if (data.length !== rowCount || data[0].length !== columnCount) {
		return tooltip.info(
			"The data to be set does not match the selection.",
			""
		);
	}

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}
	let sheetData = $.extend(true, [], file.data);

	for (let i = 0; i < rowCount; i++) {
		for (let j = 0; j < columnCount; j++) {
			let row = range.row[0] + i,
				column = range.column[0] + j;
			setCellValue(row, column, data[i][j], {
				order: order,
				isRefresh: false,
			});
		}
	}

	let fileData = $.extend(true, [], file.data);
	file.data.length = 0;
	file.data.push(...sheetData);

	if (file.index == Store.currentSheetIndex) {
		jfrefreshgrid(
			fileData,
			[
				{
					row: range.row,
					column: range.column,
				},
			],
			undefined,
			true,
			false
		);
	}

	if (isRefresh) {
		luckysheetrefreshgrid();
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 设置指定范围的单元格格式，一般用作处理格式，赋值操作推荐使用setRangeValue方法
 * @param {String} attr 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Number | String | Object} value 具体的设置值
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 设置参数的目标选区范围，支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 */
export function setSingleRangeFormat(attr, value, options = {}) {
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let curRange =
		Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1];
	let { range = curRange, order = curSheetOrder } = { ...options };

	if (!attr) {
		tooltip.info("Arguments attr cannot be null or undefined.", "");
		return "error";
	}

	if (range instanceof Array) {
		tooltip.info("setRangeValue only supports a single selection.", "");
		return "error";
	}

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			tooltip.info("The range parameter is invalid.", "");
			return "error";
		}

		range = formula.getcellrange(range);
	}

	if (
		getObjType(range) != "object" ||
		range.row == null ||
		range.column == null
	) {
		tooltip.info("The range parameter is invalid.", "");
		return "error";
	}

	for (let r = range.row[0]; r <= range.row[1]; r++) {
		for (let c = range.column[0]; c <= range.column[1]; c++) {
			console.log("r", r);
			console.log("c", c);
			setCellValue(
				r,
				c,
				{ [attr]: value },
				{
					order: order,
					isRefresh: false,
				}
			);
		}
	}
}

/**
 * 设置指定范围的单元格格式，一般用作处理格式。支持多选区设置
 * @param {String} attr 要赋值的单元格二维数组数据，每个单元格的值，可以为字符串或数字，或为符合Luckysheet格式的对象
 * @param {Number | String | Object} value 具体的设置值
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 设置参数的目标选区范围，支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeFormat(attr, value, options = {}) {
	let curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
	let { range = curRange, order = curSheetOrder, success } = { ...options };

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = [
			{
				row: cellrange.row,
				column: cellrange.column,
			},
		];
	} else if (getObjType(range) == "object") {
		if (range.row == null || range.column == null) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		range = [
			{
				row: range.row,
				column: range.column,
			},
		];
	}

	if (getObjType(range) != "array") {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let file = Store.luckysheetfile[order];

	let result = [];

	for (let i = 0; i < range.length; i++) {
		result.push(
			setSingleRangeFormat(attr, value, { range: range[i], order: order })
		);
	}

	let fileData = $.extend(true, [], file.data);
	if (result.some((i) => i === "error")) {
		file.data.length = 0;
		file.data.push(...fileData);
		return false;
	}

	file.data.length = 0;
	file.data.push(...fileData);

	if (file.index == Store.currentSheetIndex) {
		jfrefreshgrid(fileData, undefined, undefined, true, false);
	}

	luckysheetrefreshgrid();

	if (success && typeof success === "function") {
	}
}

/**
 * 为指定索引的工作表，选定的范围开启或关闭筛选功能
 * @param {String} type 打开还是关闭筛选功能  open-打开筛选功能，返回当前筛选的范围对象；close-关闭筛选功能，返回关闭前筛选的范围对象
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Object} options.success 操作结束的回调函数
 */
export function setRangeFilter(type, options = {}) {
	let typeValues = ["open", "close"];

	if (!typeValues.includes(type)) {
		return tooltip.info("The type parameter is invalid.", "");
	}

	let {
		range = Store.luckysheet_select_save[
			Store.luckysheet_select_save.length - 1
		],
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		range = formula.getcellrange(range);
	}

	if (
		getObjType(range) != "object" ||
		range.row == null ||
		range.column == null
	) {
		return tooltip.info("The range parameter is invalid.", "");
	}

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	if (type == "open") {
		file.filter_select = range;

		if (file.index == Store.currentSheetIndex) {
			createFilterOptions(range, file.filter);
		}

		return {
			row: range.row,
			column: range.column,
		};
	} else if (type == "close") {
		let luckysheet_filter_save = $.extend(true, {}, file.filter_select);

		file.filter_select = null;

		$("#luckysheet-filter-selected-sheet" + file.index).remove();
		$("#luckysheet-filter-options-sheet" + file.index).remove();

		return {
			row: luckysheet_filter_save.row,
			column: luckysheet_filter_save.column,
		};
	}
}

/**
 * 为指定索引的工作表，选定的范围设定合并单元格
 * @param {String} type 合并类型 all-全部合并  horizontal-水平合并  vertical-垂直合并
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Object} options.success 操作结束的回调函数
 */
export function setRangeMerge(type, options = {}) {
	let typeValues = ["all", "horizontal", "vertical"];
	if (typeValues.indexOf(type) < 0) {
		return tooltip.info(
			"The type parameter must be included in ['all', 'horizontal', 'vertical']",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
		curRange = JSON.parse(JSON.stringify(Store.luckysheet_select_save));
	let { range = curRange, order = curSheetOrder, success } = { ...options };

	let file = Store.luckysheetfile[order],
		cfg = $.extend(true, {}, file.config),
		data = $.extend(true, [], file.data);

	if (data.length == 0) {
		data = $.extend(true, [], sheetmanage.buildGridData(file));
	}

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("Incorrect selection format", "");
		}

		let cellrange = formula.getcellrange(range);
		range = [
			{
				row: cellrange.row,
				column: cellrange.column,
			},
		];
	} else if (getObjType(range) == "object") {
		if (!range.hasOwnProperty("row") || !range.hasOwnProperty("column")) {
			return tooltip.info("Incorrect selection format", "");
		}

		range = [
			{
				row: range.row,
				column: range.column,
			},
		];
	}

	//不能合并重叠区域
	if (selectIsOverlap(range)) {
		return tooltip.info("Cannot merge overlapping range", "");
	}

	//选区是否含有 部分合并单元格
	if (cfg["merge"] != null) {
		let has_PartMC = false;

		for (let s = 0; s < range.length; s++) {
			let r1 = range[s].row[0],
				r2 = range[s].row[1];
			let c1 = range[s].column[0],
				c2 = range[s].column[1];

			has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

			if (has_PartMC) {
				break;
			}
		}

		if (has_PartMC) {
			return tooltip.info(
				"Cannot perform this operation on partially merged cells",
				""
			);
		}
	} else {
		cfg.merge = {};
	}

	//选区是否含有 合并的单元格
	let isHasMc = false;

	for (let i = 0; i < range.length; i++) {
		let r1 = range[i].row[0],
			r2 = range[i].row[1];
		let c1 = range[i].column[0],
			c2 = range[i].column[1];

		for (let r = r1; r <= r2; r++) {
			for (let c = c1; c <= c2; c++) {
				let cell = data[r][c];

				if (getObjType(cell) == "object" && "mc" in cell) {
					isHasMc = true;
					break;
				}
			}

			if (isHasMc) {
				break;
			}
		}
	}

	if (isHasMc) {
		//选区有合并单元格（选区都执行 取消合并）
		cancelRangeMerge({
			range: range,
			order: order,
		});
	} else {
		for (let i = 0; i < range.length; i++) {
			let r1 = range[i].row[0],
				r2 = range[i].row[1];
			let c1 = range[i].column[0],
				c2 = range[i].column[1];

			if (r1 == r2 && c1 == c2) {
				continue;
			}

			if (type == "all") {
				let fv = {},
					isfirst = false;

				for (let r = r1; r <= r2; r++) {
					for (let c = c1; c <= c2; c++) {
						let cell = data[r][c];

						if (
							cell != null &&
							(!isRealNull(cell.v) || cell.f != null) &&
							!isfirst
						) {
							fv = $.extend(true, {}, cell);
							isfirst = true;
						}

						data[r][c] = { mc: { r: r1, c: c1 } };
					}
				}

				data[r1][c1] = fv;
				data[r1][c1].mc = {
					r: r1,
					c: c1,
					rs: r2 - r1 + 1,
					cs: c2 - c1 + 1,
				};

				cfg["merge"][r1 + "_" + c1] = {
					r: r1,
					c: c1,
					rs: r2 - r1 + 1,
					cs: c2 - c1 + 1,
				};
			} else if (type == "vertical") {
				for (let c = c1; c <= c2; c++) {
					let fv = {},
						isfirst = false;

					for (let r = r1; r <= r2; r++) {
						let cell = data[r][c];

						if (
							cell != null &&
							(!isRealNull(cell.v) || cell.f != null) &&
							!isfirst
						) {
							fv = $.extend(true, {}, cell);
							isfirst = true;
						}

						data[r][c] = { mc: { r: r1, c: c } };
					}

					data[r1][c] = fv;
					data[r1][c].mc = { r: r1, c: c, rs: r2 - r1 + 1, cs: 1 };

					cfg["merge"][r1 + "_" + c] = {
						r: r1,
						c: c,
						rs: r2 - r1 + 1,
						cs: 1,
					};
				}
			} else if (type == "horizontal") {
				for (let r = r1; r <= r2; r++) {
					let fv = {},
						isfirst = false;

					for (let c = c1; c <= c2; c++) {
						let cell = data[r][c];

						if (
							cell != null &&
							(!isRealNull(cell.v) || cell.f != null) &&
							!isfirst
						) {
							fv = $.extend(true, {}, cell);
							isfirst = true;
						}

						data[r][c] = { mc: { r: r, c: c1 } };
					}

					data[r][c1] = fv;
					data[r][c1].mc = { r: r, c: c1, rs: 1, cs: c2 - c1 + 1 };

					cfg["merge"][r + "_" + c1] = {
						r: r,
						c: c1,
						rs: 1,
						cs: c2 - c1 + 1,
					};
				}
			}
		}

		if (order == curSheetOrder) {
			if (Store.clearjfundo) {
				Store.jfundo.length = 0;
				Store.jfredo.push({
					type: "mergeChange",
					sheetIndex: file.index,
					data: $.extend(true, [], file.data),
					curData: data,
					range: range,
					config: $.extend(true, {}, file.config),
					curConfig: cfg,
				});
			}

			Store.clearjfundo = false;
			jfrefreshgrid(data, range, { cfg: cfg });
			Store.clearjfundo = true;
		} else {
			file.data = data;
			file.config = cfg;
		}
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 为指定索引的工作表，选定的范围取消合并单元格
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Object} options.success 操作结束的回调函数
 */
export function cancelRangeMerge(options = {}) {
	let curRange = Store.luckysheet_select_save,
		curSheetOrder = getSheetIndex(Store.currentSheetIndex);
	let { range = curRange, order = curSheetOrder, success } = { ...options };

	let file = Store.luckysheetfile[order],
		cfg = $.extend(true, {}, file.config),
		data = $.extend(true, [], file.data);

	if (data.length == 0) {
		data = $.extend(true, [], sheetmanage.buildGridData(file));
	}

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("Incorrect selection format", "");
		}

		let cellrange = formula.getcellrange(range);
		range = [
			{
				row: cellrange.row,
				column: cellrange.column,
			},
		];
	} else if (getObjType(range) == "object") {
		if (!range.hasOwnProperty("row") || !range.hasOwnProperty("column")) {
			return tooltip.info("Incorrect selection format", "");
		}

		range = [
			{
				row: range.row,
				column: range.column,
			},
		];
	}

	//不能合并重叠区域
	if (selectIsOverlap(range)) {
		return tooltip.info("Cannot merge overlapping range", "");
	}

	//选区是否含有 部分合并单元格
	if (cfg["merge"] != null) {
		let has_PartMC = false;

		for (let s = 0; s < range.length; s++) {
			let r1 = range[s].row[0],
				r2 = range[s].row[1];
			let c1 = range[s].column[0],
				c2 = range[s].column[1];

			has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

			if (has_PartMC) {
				break;
			}
		}

		if (has_PartMC) {
			return tooltip.info(
				"Cannot perform this operation on partially merged cells",
				""
			);
		}
	}

	for (let i = 0; i < range.length; i++) {
		let r1 = range[i].row[0],
			r2 = range[i].row[1];
		let c1 = range[i].column[0],
			c2 = range[i].column[1];

		if (r1 == r2 && c1 == c2) {
			continue;
		}

		let fv = {};

		for (let r = r1; r <= r2; r++) {
			for (let c = c1; c <= c2; c++) {
				let cell = data[r][c];

				if (cell != null && cell.mc != null) {
					let mc_r = cell.mc.r,
						mc_c = cell.mc.c;

					if ("rs" in cell.mc) {
						delete cell.mc;
						delete cfg["merge"][mc_r + "_" + mc_c];

						fv[mc_r + "_" + mc_c] = $.extend(true, {}, cell);
					} else {
						// let cell_clone = fv[mc_r + "_" + mc_c];
						let cell_clone = JSON.parse(
							JSON.stringify(fv[mc_r + "_" + mc_c])
						);

						delete cell_clone.v;
						delete cell_clone.m;
						delete cell_clone.ct;
						delete cell_clone.f;
						delete cell_clone.spl;

						data[r][c] = cell_clone;
					}
				}
			}
		}
	}

	if (order == curSheetOrder) {
		if (Store.clearjfundo) {
			Store.jfundo.length = 0;
			Store.jfredo.push({
				type: "mergeChange",
				sheetIndex: file.index,
				data: $.extend(true, [], file.data),
				curData: data,
				range: range,
				config: $.extend(true, {}, file.config),
				curConfig: cfg,
			});
		}

		Store.clearjfundo = false;
		jfrefreshgrid(data, range, { cfg: cfg });
		Store.clearjfundo = true;
	} else {
		file.data = data;
		file.config = cfg;
	}
}

/**
 * 为指定索引的工作表，选定的范围开启排序功能，返回选定范围排序后的数据。
 * @param {String} type 排序类型 asc-升序 desc-降序
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeSort(type, options = {}) {
	let typeValues = ["asc", "desc"];
	if (typeValues.indexOf(type) < 0) {
		return tooltip.info(
			"The type parameter must be included in ['asc', 'desc'",
			""
		);
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
		curRange = Store.luckysheet_select_save[0];
	let { range = curRange, order = curSheetOrder, success } = { ...options };

	let file = Store.luckysheetfile[order],
		cfg = $.extend(true, {}, file.config),
		fileData = $.extend(true, [], file.data);

	if (fileData.length == 0) {
		fileData = $.extend(true, [], sheetmanage.buildGridData(file));
	}

	if (range instanceof Array && range.length > 1) {
		tooltip.info(locale().sort.noRangeError, "");
		return;
	}

	if (range && typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	let r1 = range.row[0],
		r2 = range.row[1],
		c1 = range.column[0],
		c2 = range.column[1];

	let hasMc = false; //Whether the sort selection has merged cells
	let data = [];
	for (let r = r1; r <= r2; r++) {
		let data_row = [];
		for (let c = c1; c <= c2; c++) {
			if (fileData[r][c] != null && fileData[r][c].mc != null) {
				hasMc = true;
				break;
			}
			data_row.push(fileData[r][c]);
		}
		data.push(data_row);
	}

	if (hasMc) {
		tooltip.info(locale().sort.mergeError, "");
		return;
	}

	data = orderbydata([].concat(data), 0, type === "asc");

	for (let r = r1; r <= r2; r++) {
		for (let c = c1; c <= c2; c++) {
			fileData[r][c] = data[r - r1][c - c1];
		}
	}

	let allParam = {};
	if (cfg["rowlen"] != null) {
		cfg = rowlenByRange(fileData, r1, r2, cfg);

		allParam = {
			cfg: cfg,
			RowlChange: true,
		};
	}

	if (file.index == Store.currentSheetIndex) {
		jfrefreshgrid(
			fileData,
			[{ row: [r1, r2], column: [c1, c2] }],
			allParam
		);
	} else {
		file.data = fileData;
		file.config = cfg;
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 为指定索引的工作表，选定的范围开启多列自定义排序功能，返回选定范围排序后的数据。
 * @param {Boolean} hasTitle 数据是否具有标题行
 * @param {Array} sort 列设置，设置需要排序的列索引和排序方式，格式如：[{ i:0,sort:'asc' },{ i:1,sort:'des' }]
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeSortMulti(hasTitle, sort, options = {}) {
	if (!sort || !(sort instanceof Array)) {
		return tooltip.info("The sort parameter is invalid.", "");
	}

	let curSheetOrder = getSheetIndex(Store.currentSheetIndex),
		curRange = Store.luckysheet_select_save[0];
	let { range = curRange, order = curSheetOrder, success } = { ...options };

	let file = Store.luckysheetfile[order],
		cfg = $.extend(true, {}, file.config),
		fileData = $.extend(true, [], file.data);

	if (fileData.length == 0) {
		fileData = $.extend(true, [], sheetmanage.buildGridData(file));
	}

	if (range instanceof Array && range.length > 1) {
		tooltip.info(locale().sort.noRangeError, "");
		return;
	}

	if (range && typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	let r1 = range.row[0],
		r2 = range.row[1],
		c1 = range.column[0],
		c2 = range.column[1];

	let str;
	if (hasTitle) {
		str = r1 + 1;
	} else {
		str = r1;
	}

	let hasMc = false; //Whether the sort selection has merged cells
	let data = [];
	for (let r = str; r <= r2; r++) {
		let data_row = [];
		for (let c = c1; c <= c2; c++) {
			if (fileData[r][c] != null && fileData[r][c].mc != null) {
				hasMc = true;
				break;
			}
			data_row.push(fileData[r][c]);
		}
		data.push(data_row);
	}

	if (hasMc) {
		tooltip.info(locale().sort.mergeError, "");
		return;
	}

	sort.forEach((sortItem) => {
		let i = sortItem.i;
		i -= c1;
		data = orderbydata([].concat(data), i, sortItem.sort === "asc");
	});

	for (let r = str; r <= r2; r++) {
		for (let c = c1; c <= c2; c++) {
			fileData[r][c] = data[r - str][c - c1];
		}
	}

	let allParam = {};
	if (cfg["rowlen"] != null) {
		cfg = rowlenByRange(fileData, str, r2, cfg);

		allParam = {
			cfg: cfg,
			RowlChange: true,
		};
	}

	if (file.index === Store.currentSheetIndex) {
		jfrefreshgrid(
			fileData,
			[{ row: [str, r2], column: [c1, c2] }],
			allParam
		);
	} else {
		file.data = fileData;
		file.config = cfg;
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 *  为指定索引的工作表，选定的范围开启条件格式，根据设置的条件格式规则突出显示部分单元格，返回开启条件格式后的数据。
 * @param {String} conditionName 条件格式规则类型
 * @param {Object} conditionValue 可以设置条件单元格或者条件值
 * @param {Object} options 可选参数
 * @param {Object} options.format 颜色设置
 * @param {Array | Object | String} options.cellrange 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeConditionalFormatDefault(
	conditionName,
	conditionValue,
	options = {}
) {
	let conditionNameValues = [
		"greaterThan",
		"lessThan",
		"betweenness",
		"equal",
		"textContains",
		"occurrenceDate",
		"duplicateValue",
		"top10",
		"top10%",
		"last10",
		"last10%",
		"AboveAverage",
		"SubAverage",
	];

	if (!conditionName || !conditionNameValues.includes(conditionName)) {
		return tooltip.info("The conditionName parameter is invalid.", "");
	}

	if (getObjType(conditionValue) != "array" || conditionValue.length == 0) {
		return tooltip.info("The conditionValue parameter is invalid.", "");
	}

	let {
		format = {
			textColor: "#000000",
			cellColor: "#ff0000",
		},
		cellrange = Store.luckysheet_select_save,
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	cellrange = JSON.parse(JSON.stringify(cellrange));

	let file = Store.luckysheetfile[order];
	let data = file.data;

	if (data == null || data.length == 0) {
		data = sheetmanage.buildGridData(file);
	}

	if (file == null) {
		return tooltip.info("Incorrect worksheet index", "");
	}

	const conditionformat_Text = locale().conditionformat;

	let conditionRange = [],
		conditionValue2 = [];

	if (conditionName == "betweenness") {
		let v1 = conditionValue[0];
		let v2 = conditionValue[1];

		//条件值是否是选区
		let rangeArr1 = conditionformat.getRangeByTxt(v1);
		if (rangeArr1.length > 1) {
			conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
			return;
		} else if (rangeArr1.length == 1) {
			let r1 = rangeArr1[0].row[0],
				r2 = rangeArr1[0].row[1];
			let c1 = rangeArr1[0].column[0],
				c2 = rangeArr1[0].column[1];

			if (r1 == r2 && c1 == c2) {
				v1 = getcellvalue(r1, c1, data);

				conditionRange.push({
					row: rangeArr1[0].row,
					column: rangeArr1[0].column,
				});
				conditionValue2.push(v1);
			} else {
				conditionformat.infoDialog(
					conditionformat_Text.onlySingleCell,
					""
				);
				return;
			}
		} else if (rangeArr1.length == 0) {
			if (isNaN(v1) || v1 == "") {
				conditionformat.infoDialog(
					conditionformat_Text.conditionValueCanOnly,
					""
				);
				return;
			} else {
				conditionValue2.push(v1);
			}
		}

		let rangeArr2 = conditionformat.getRangeByTxt(v2);
		if (rangeArr2.length > 1) {
			conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
			return;
		} else if (rangeArr2.length == 1) {
			let r1 = rangeArr2[0].row[0],
				r2 = rangeArr2[0].row[1];
			let c1 = rangeArr2[0].column[0],
				c2 = rangeArr2[0].column[1];

			if (r1 == r2 && c1 == c2) {
				v2 = getcellvalue(r1, c1, data);

				conditionRange.push({
					row: rangeArr2[0].row,
					column: rangeArr2[0].column,
				});
				conditionValue2.push(v2);
			} else {
				conditionformat.infoDialog(
					conditionformat_Text.onlySingleCell,
					""
				);
				return;
			}
		} else if (rangeArr2.length == 0) {
			if (isNaN(v2) || v2 == "") {
				conditionformat.infoDialog(
					conditionformat_Text.conditionValueCanOnly,
					""
				);
				return;
			} else {
				conditionValue2.push(v2);
			}
		}
	} else if (
		conditionName == "greaterThan" ||
		conditionName == "lessThan" ||
		conditionName == "equal"
	) {
		let v = conditionValue[0];

		//条件值是否是选区
		let rangeArr = conditionformat.getRangeByTxt(v);
		if (rangeArr.length > 1) {
			conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
			return;
		} else if (rangeArr.length == 1) {
			let r1 = rangeArr[0].row[0],
				r2 = rangeArr[0].row[1];
			let c1 = rangeArr[0].column[0],
				c2 = rangeArr[0].column[1];

			if (r1 == r2 && c1 == c2) {
				v = getcellvalue(r1, c1, data);

				conditionRange.push({
					row: rangeArr[0].row,
					column: rangeArr[0].column,
				});
				conditionValue2.push(v);
			} else {
				conditionformat.infoDialog(
					conditionformat_Text.onlySingleCell,
					""
				);
				return;
			}
		} else if (rangeArr.length == 0) {
			if (isNaN(v) || v == "") {
				conditionformat.infoDialog(
					conditionformat_Text.conditionValueCanOnly,
					""
				);
				return;
			} else {
				conditionValue2.push(v);
			}
		}
	} else if (conditionName == "textContains") {
		let v = conditionValue[0];

		//条件值是否是选区
		let rangeArr = conditionformat.getRangeByTxt(v);
		if (rangeArr.length > 1) {
			conditionformat.infoDialog(conditionformat_Text.onlySingleCell, "");
			return;
		} else if (rangeArr.length == 1) {
			let r1 = rangeArr[0].row[0],
				r2 = rangeArr[0].row[1];
			let c1 = rangeArr[0].column[0],
				c2 = rangeArr[0].column[1];

			if (r1 == r2 && c1 == c2) {
				v = getcellvalue(r1, c1, data);

				conditionRange.push({
					row: rangeArr[0].row,
					column: rangeArr[0].column,
				});
				conditionValue2.push(v);
			} else {
				conditionformat.infoDialog(
					conditionformat_Text.onlySingleCell,
					""
				);
				return;
			}
		} else if (rangeArr.length == 0) {
			if (v == "") {
				conditionformat.infoDialog(
					conditionformat_Text.conditionValueCanOnly,
					""
				);
				return;
			} else {
				conditionValue2.push(v);
			}
		}
	} else if (conditionName == "occurrenceDate") {
		let v1 = conditionValue[0];
		let v2 = conditionValue[1];

		if (!isdatetime(v1) || !isdatetime(v2)) {
			return tooltip.info("The conditionValue parameter is invalid.", "");
		}

		let v;
		if (diff(v1, v2) > 0) {
			v =
				dayjs(v2).format("YYYY/MM/DD") +
				"-" +
				dayjs(v1).format("YYYY/MM/DD");
		} else {
			v =
				dayjs(v1).format("YYYY/MM/DD") +
				"-" +
				dayjs(v2).format("YYYY/MM/DD");
		}

		conditionValue2.push(v);
	} else if (conditionName == "duplicateValue") {
		let v = conditionValue[0];

		if (v != "0" || v != "1") {
			return tooltip.info("The conditionValue parameter is invalid.", "");
		}

		conditionValue2.push(v);
	} else if (
		conditionName == "top10" ||
		conditionName == "top10%" ||
		conditionName == "last10" ||
		conditionName == "last10%"
	) {
		let v = conditionValue[0];

		if (parseInt(v) != v || parseInt(v) < 1 || parseInt(v) > 1000) {
			conditionformat.infoDialog(
				conditionformat_Text.pleaseEnterInteger,
				""
			);
			return;
		}

		conditionValue2.push(parseInt(v));
	} else if (
		conditionName == "AboveAverage" ||
		conditionName == "SubAverage"
	) {
		conditionValue2.push(conditionName);
	}

	if (
		!format.hasOwnProperty("textColor") ||
		!format.hasOwnProperty("cellColor")
	) {
		return tooltip.info("The format parameter is invalid.", "");
	}

	if (getObjType(cellrange) == "string") {
		cellrange = conditionformat.getRangeByTxt(cellrange);
	} else if (getObjType(cellrange) == "object") {
		cellrange = [cellrange];
	}

	if (getObjType(cellrange) != "array") {
		return tooltip.info("The cellrange parameter is invalid.", "");
	}

	let rule = {
		type: "default",
		cellrange: cellrange,
		format: format,
		conditionName: conditionName,
		conditionRange: conditionRange,
		conditionValue: conditionValue2,
	};

	//保存之前的规则
	let fileH = $.extend(true, [], Store.luckysheetfile);
	let historyRules = conditionformat.getHistoryRules(fileH);

	//保存当前的规则
	let ruleArr = file["luckysheet_conditionformat_save"] || [];
	ruleArr.push(rule);
	file["luckysheet_conditionformat_save"] = ruleArr;

	let fileC = $.extend(true, [], Store.luckysheetfile);
	let currentRules = conditionformat.getCurrentRules(fileC);

	//刷新一次表格
	conditionformat.ref(historyRules, currentRules);

	//发送给后台
	if (server.allowUpdate) {
		server.saveParam("all", file.index, ruleArr, {
			k: "luckysheet_conditionformat_save",
		});
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 为指定索引的工作表，选定的范围开启条件格式，返回开启条件格式后的数据。
 * @param {String} type 条件格式规则类型
 * @param {Object} options 可选参数
 * @param {Array | String} options.format 颜色设置
 * @param {Array | Object | String} options.cellrange 选区范围
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setRangeConditionalFormat(type, options = {}) {
	let typeValues = ["dataBar", "colorGradation", "icons"];

	if (!type || !typeValues.includes(type)) {
		return tooltip.info("The type parameter is invalid.", "");
	}

	let {
		format,
		cellrange = Store.luckysheet_select_save,
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	cellrange = JSON.parse(JSON.stringify(cellrange));
	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("Incorrect worksheet index", "");
	}

	if (type == "dataBar") {
		if (format == null) {
			format = ["#638ec6", "#ffffff"];
		}

		if (
			getObjType(format) != "array" ||
			format.length < 1 ||
			format.length > 2
		) {
			return tooltip.info("The format parameter is invalid.", "");
		}
	} else if (type == "colorGradation") {
		if (format == null) {
			format = [
				"rgb(99, 190, 123)",
				"rgb(255, 235, 132)",
				"rgb(248, 105, 107)",
			];
		}

		if (
			getObjType(format) != "array" ||
			format.length < 2 ||
			format.length > 3
		) {
			return tooltip.info("The format parameter is invalid.", "");
		}
	} else if (type == "icons") {
		if (format == null) {
			format = "threeWayArrowMultiColor";
		}

		let formatValues = [
			"threeWayArrowMultiColor",
			"threeTriangles",
			"fourWayArrowMultiColor",
			"fiveWayArrowMultiColor",
			"threeWayArrowGrayColor",
			"fourWayArrowGrayColor",
			"fiveWayArrowGrayColor",
			"threeColorTrafficLightRimless",
			"threeSigns",
			"greenRedBlackGradient",
			"threeColorTrafficLightBordered",
			"fourColorTrafficLight",
			"threeSymbolsCircled",
			"tricolorFlag",
			"threeSymbolsnoCircle",
			"threeStars",
			"fiveQuadrantDiagram",
			"fiveBoxes",
			"grade4",
			"grade5",
		];

		if (getObjType(format) != "string" || !formatValues.includes(format)) {
			return tooltip.info("The format parameter is invalid.", "");
		}

		switch (format) {
			case "threeWayArrowMultiColor":
				format = {
					len: 3,
					leftMin: 0,
					top: 0,
				};
				break;
			case "threeTriangles":
				format = {
					len: 3,
					leftMin: 0,
					top: 1,
				};
				break;
			case "fourWayArrowMultiColor":
				format = {
					len: 4,
					leftMin: 0,
					top: 2,
				};
				break;
			case "fiveWayArrowMultiColor":
				format = {
					len: 5,
					leftMin: 0,
					top: 3,
				};
				break;
			case "threeWayArrowGrayColor":
				format = {
					len: 3,
					leftMin: 5,
					top: 0,
				};
				break;
			case "fourWayArrowGrayColor":
				format = {
					len: 4,
					leftMin: 5,
					top: 1,
				};
				break;
			case "fiveWayArrowGrayColor":
				format = {
					len: 5,
					leftMin: 5,
					top: 2,
				};
				break;
			case "threeColorTrafficLightRimless":
				format = {
					len: 3,
					leftMin: 0,
					top: 4,
				};
				break;
			case "threeSigns":
				format = {
					len: 3,
					leftMin: 0,
					top: 5,
				};
				break;
			case "greenRedBlackGradient":
				format = {
					len: 4,
					leftMin: 0,
					top: 6,
				};
				break;
			case "threeColorTrafficLightBordered":
				format = {
					len: 3,
					leftMin: 5,
					top: 4,
				};
				break;
			case "fourColorTrafficLight":
				format = {
					len: 4,
					leftMin: 5,
					top: 5,
				};
				break;
			case "threeSymbolsCircled":
				format = {
					len: 3,
					leftMin: 0,
					top: 7,
				};
				break;
			case "tricolorFlag":
				format = {
					len: 3,
					leftMin: 0,
					top: 8,
				};
				break;
			case "threeSymbolsnoCircle":
				format = {
					len: 3,
					leftMin: 5,
					top: 7,
				};
				break;
			case "threeStars":
				format = {
					len: 3,
					leftMin: 0,
					top: 9,
				};
				break;
			case "fiveQuadrantDiagram":
				format = {
					len: 5,
					leftMin: 0,
					top: 10,
				};
				break;
			case "fiveBoxes":
				format = {
					len: 5,
					leftMin: 0,
					top: 11,
				};
				break;
			case "grade4":
				format = {
					len: 4,
					leftMin: 5,
					top: 9,
				};
				break;
			case "grade5":
				format = {
					len: 5,
					leftMin: 5,
					top: 10,
				};
				break;
		}
	}

	if (getObjType(cellrange) == "string") {
		cellrange = conditionformat.getRangeByTxt(cellrange);
	} else if (getObjType(cellrange) == "object") {
		cellrange = [cellrange];
	}

	if (getObjType(cellrange) != "array") {
		return tooltip.info("The cellrange parameter is invalid.", "");
	}

	let rule = {
		type: type,
		cellrange: cellrange,
		format: format,
	};

	//保存之前的规则
	let fileH = $.extend(true, [], Store.luckysheetfile);
	let historyRules = conditionformat.getHistoryRules(fileH);

	//保存当前的规则
	let ruleArr = file["luckysheet_conditionformat_save"] || [];
	ruleArr.push(rule);
	file["luckysheet_conditionformat_save"] = ruleArr;

	let fileC = $.extend(true, [], Store.luckysheetfile);
	let currentRules = conditionformat.getCurrentRules(fileC);

	//刷新一次表格
	conditionformat.ref(historyRules, currentRules);

	//发送给后台
	if (server.allowUpdate) {
		server.saveParam("all", file.index, ruleArr, {
			k: "luckysheet_conditionformat_save",
		});
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 为指定下标的工作表，删除条件格式规则，返回被删除的条件格式规则
 * @param {Number} itemIndex 条件格式规则索引
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRangeConditionalFormat(itemIndex, options = {}) {
	if (!isRealNum(itemIndex)) {
		return tooltip.info("The itemIndex parameter is invalid.", "");
	}

	itemIndex = Number(itemIndex);

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let cdformat = $.extend(true, [], file.luckysheet_conditionformat_save);

	if (cdformat.length == 0) {
		return tooltip.info(
			"This worksheet has no conditional format to delete",
			""
		);
	} else if (cdformat[itemIndex] == null) {
		return tooltip.info(
			"The conditional format of the index cannot be found",
			""
		);
	}

	let cdformatItem = cdformat.splice(itemIndex, 1);

	//保存之前的规则
	let fileH = $.extend(true, [], Store.luckysheetfile);
	let historyRules = conditionformat.getHistoryRules(fileH);

	//保存当前的规则
	file["luckysheet_conditionformat_save"] = cdformat;

	let fileC = $.extend(true, [], Store.luckysheetfile);
	let currentRules = conditionformat.getCurrentRules(fileC);

	//刷新一次表格
	conditionformat.ref(historyRules, currentRules);

	//发送给后台
	if (server.allowUpdate) {
		server.saveParam("all", file.index, ruleArr, {
			k: "luckysheet_conditionformat_save",
		});
	}

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return cdformatItem;
}

/**
 * 清除指定工作表指定单元格区域的内容，不同于删除选区的功能，不需要设定单元格移动情况
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 要清除的选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function clearRange(options = {}) {
	let {
		range = Store.luckysheet_select_save,
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	range = JSON.parse(JSON.stringify(range));
	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = [
			{
				row: cellrange.row,
				column: cellrange.column,
			},
		];
	} else if (getObjType(range) == "object") {
		if (range.row == null || range.column == null) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		range = [
			{
				row: range.row,
				column: range.column,
			},
		];
	}

	if (getObjType(range) != "array") {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let cfg = $.extend(true, {}, file.config);
	let has_PartMC = false;

	for (let s = 0; s < range.length; s++) {
		let r1 = range[s].row[0],
			r2 = range[s].row[1];
		let c1 = range[s].column[0],
			c2 = range[s].column[1];

		has_PartMC = hasPartMC(cfg, r1, r2, c1, c2);

		if (has_PartMC) {
			break;
		}
	}

	if (has_PartMC) {
		return tooltip.info(
			"Cannot perform this operation on partially merged cells",
			""
		);
	}

	let d = $.extend(true, [], file.data);

	if (d.length == 0) {
		d = $.extend(true, [], sheetmanage.buildGridData(file));
	}

	for (let s = 0; s < range.length; s++) {
		let r1 = range[s].row[0],
			r2 = range[s].row[1];
		let c1 = range[s].column[0],
			c2 = range[s].column[1];

		for (let r = r1; r <= r2; r++) {
			for (let c = c1; c <= c2; c++) {
				let cell = d[r][c];

				if (getObjType(cell) == "object") {
					delete cell["m"];
					delete cell["v"];

					if (cell["f"] != null) {
						delete cell["f"];
						formula.delFunctionGroup(r, c, file.index);

						delete cell["spl"];
					}

					if (cell["ct"] != null && cell["ct"].t == "inlineStr") {
						delete cell["ct"];
					}
				} else {
					d[r][c] = null;
				}
			}
		}
	}

	if (file.index == Store.currentSheetIndex) {
		jfrefreshgrid(d, range);
	} else {
		file.data = d;
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 删除指定工作表指定单元格区域，返回删除掉的数据，同时，指定是右侧单元格左移还是下方单元格上移
 * @param {String} move 删除后，单元格左移/上移
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 要删除的选区范围
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteRange(move, options = {}) {
	let moveList = ["left", "up"];

	if (!moveList.includes(move)) {
		return tooltip.info("The move parameter is invalid.", "");
	}

	let {
		range = Store.luckysheet_select_save[
			Store.luckysheet_select_save.length - 1
		],
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = {
			row: cellrange.row,
			column: cellrange.column,
		};
	}

	if (
		getObjType(range) != "object" ||
		range.row == null ||
		range.column == null
	) {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let str = range.row[0],
		edr = range.row[1],
		stc = range.column[0],
		edc = range.column[1];

	if (move == "left") {
		luckysheetDeleteCell("moveLeft", str, edr, stc, edc, order);
	} else if (move == "up") {
		luckysheetDeleteCell("moveUp", str, edr, stc, edc, order);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 指定工作表指定单元格区域的数据进行矩阵操作，返回操作成功后的结果数据
 * @param {String} type 矩阵操作的类型
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Function} options.success 操作结束的回调函数
 */
export function matrixOperation(type, options = {}) {
	let typeValues = [
		"flipUpDown", // 上下翻转
		"flipLeftRight", // 左右翻转
		"flipClockwise", // 顺时针旋转
		"flipCounterClockwise", // 逆时针旋转
		"transpose", // 转置
		"deleteZeroByRow", // 按行删除两端0值
		"deleteZeroByColumn", // 按列删除两端0值
		"removeDuplicateByRow", // 按行删除重复值
		"removeDuplicateByColumn", // 按列删除重复值
		"newMatrix", // 生产新矩阵
	];

	if (!type || typeValues.indexOf(type) < 0) {
		return tooltip.info("The type parameter is invalid.", "");
	}

	let curRange = Store.luckysheet_select_save[0];
	let { range = curRange, success } = { ...options };

	if (range instanceof Array && range.length > 1) {
		tooltip.info(locale().drag.noMulti, "");
		return;
	}

	if (range && typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	let getdata = getdatabyselection(range);
	let arr = [];
	if (getdata.length === 0) {
		return;
	}

	let getdatalen, collen, arr1;
	switch (type) {
		case "flipUpDown":
			for (let r = getdata.length - 1; r >= 0; r--) {
				let a = [];
				for (let c = 0; c < getdata[0].length; c++) {
					let value = "";
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];
					}
					a.push(value);
				}
				arr.push(a);
			}
			break;
		case "flipLeftRight":
			for (let r = 0; r < getdata.length; r++) {
				let a = [];
				for (let c = getdata[0].length - 1; c >= 0; c--) {
					let value = "";
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];
					}
					a.push(value);
				}
				arr.push(a);
			}
			break;
		case "flipClockwise":
			for (let c = 0; c < getdata[0].length; c++) {
				let a = [];
				for (let r = getdata.length - 1; r >= 0; r--) {
					let value = "";
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];
					}
					a.push(value);
				}
				arr.push(a);
			}
			break;
		case "flipCounterClockwise":
			for (let c = getdata[0].length - 1; c >= 0; c--) {
				let a = [];
				for (let r = 0; r < getdata.length; r++) {
					let value = "";
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];
					}
					a.push(value);
				}
				arr.push(a);
			}
			break;
		case "transpose":
			for (let c = 0; c < getdata[0].length; c++) {
				let a = [];
				for (let r = 0; r < getdata.length; r++) {
					let value = "";
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];
					}
					a.push(value);
				}
				arr.push(a);
			}
			break;
		case "deleteZeroByRow":
			getdatalen = getdata[0].length;
			for (let r = 0; r < getdata.length; r++) {
				let a = [],
					stdel = true,
					eddel = true;
				for (let c = 0; c < getdatalen; c++) {
					let value = "";
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];
						if ((value.v == "0" || value.v == 0) && stdel) {
							continue;
						} else {
							stdel = false;
						}
					}
					a.push(value);
				}

				let a1 = [];
				if (a.length == getdatalen) {
					a1 = a;
				} else {
					for (let c = a.length - 1; c >= 0; c--) {
						let value = "";
						if (a[c] != null) {
							value = a[c];
							if ((value.v == "0" || value.v == 0) && eddel) {
								continue;
							} else {
								eddel = false;
							}
						}
						a1.unshift(value);
					}

					let l = getdatalen - a1.length;
					for (let c1 = 0; c1 < l; c1++) {
						a1.push("");
					}
				}
				arr.push(a1);
			}
			break;
		case "deleteZeroByColumn":
			getdatalen = getdata.length;
			collen = getdata[0].length;
			for (let c = 0; c < collen; c++) {
				let a = [],
					stdel = true,
					eddel = true;
				for (let r = 0; r < getdatalen; r++) {
					let value = "";
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];
						if ((value.v == "0" || value.v == 0) && stdel) {
							continue;
						} else {
							stdel = false;
						}
					}
					a.push(value);
				}

				let a1 = [];
				if (a.length == getdatalen) {
					a1 = a;
				} else {
					for (let r = a.length - 1; r >= 0; r--) {
						let value = "";
						if (a[r] != null) {
							value = a[r];
							if ((value.v == "0" || value.v == 0) && eddel) {
								continue;
							} else {
								eddel = false;
							}
						}
						a1.unshift(value);
					}

					let l = getdatalen - a1.length;
					for (let r1 = 0; r1 < l; r1++) {
						a1.push("");
					}
				}
				arr.push(a1);
			}

			arr1 = [];
			for (let c = 0; c < arr[0].length; c++) {
				let a = [];
				for (let r = 0; r < arr.length; r++) {
					let value = "";
					if (arr[r] != null && arr[r][c] != null) {
						value = arr[r][c];
					}
					a.push(value);
				}
				arr1.push(a);
			}
			break;
		case "removeDuplicateByRow":
			getdatalen = getdata[0].length;
			for (let r = 0; r < getdata.length; r++) {
				let a = [],
					repeat = {};

				for (let c = 0; c < getdatalen; c++) {
					let value = null;
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];

						if (value.v in repeat) {
							repeat[value.v].push(value);
						} else {
							repeat[value.v] = [];
							repeat[value.v].push(value);
						}
					}
				}

				for (let c = 0; c < getdatalen; c++) {
					let value = null;
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];

						if (repeat[value.v].length == 1) {
							a.push(value);
						}
					}
				}

				let l = getdatalen - a.length;
				for (let c1 = 0; c1 < l; c1++) {
					a.push(null);
				}
				arr.push(a);
			}
			break;
		case "removeDuplicateByColumn":
			collen = getdata[0].length;
			getdatalen = getdata.length;
			for (let c = 0; c < collen; c++) {
				let a = [],
					repeat = {};

				for (let r = 0; r < getdatalen; r++) {
					let value = null;
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];

						if (value.v in repeat) {
							repeat[value.v].push(value);
						} else {
							repeat[value.v] = [];
							repeat[value.v].push(value);
						}
					}
				}

				for (let r = 0; r < getdatalen; r++) {
					let value = null;
					if (getdata[r] != null && getdata[r][c] != null) {
						value = getdata[r][c];

						if (repeat[value.v].length == 1) {
							a.push(value);
						}
					}
				}

				a1 = a;
				let l = getdatalen - a1.length;
				for (let r1 = 0; r1 < l; r1++) {
					a1.push(null);
				}
				arr.push(a1);
			}

			arr1 = [];
			for (let c = 0; c < arr[0].length; c++) {
				let a = [];
				for (let r = 0; r < arr.length; r++) {
					let value = null;
					if (arr[r] != null && arr[r][c] != null) {
						value = arr[r][c];
					}
					a.push(value);
				}
				arr1.push(a);
			}
			break;
		case "newMatrix":
			// TODO
			console.log("TODO");
			break;
	}
	editor.controlHandler(arr, range);

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 指定工作表指定单元格区域的数据进行矩阵计算，返回计算成功后的结果数据
 * @param {String} type 计算方式
 * @param {Number} number 计算数值
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,支持选区的格式为"A1:B2"、"sheetName!A1:B2"或者{row:[0,1],column:[0,1]}，只能为单个选区；默认为当前选区
 * @param {Function} options.success 操作结束的回调函数
 */
export function matrixCalculation(type, number, options = {}) {
	let typeValues = [
		"plus", // 加
		"minus", // 减
		"multiply", // 乘
		"divided", // 除
		"power", // 幂
		"root", // 次方根
		"log", // 对数log
	];

	if (!type || typeValues.indexOf(type) < 0) {
		return tooltip.info("The type parameter is invalid.", "");
	}

	if (!isRealNum(number)) {
		return tooltip.info("The number parameter is invalid.", "");
	}

	let curRange = Store.luckysheet_select_save[0];
	let { range = curRange, success } = { ...options };

	if (range instanceof Array && range.length > 1) {
		tooltip.info(locale().drag.noMulti, "");
		return;
	}

	if (range && typeof range === "string" && formula.iscelldata(range)) {
		range = formula.getcellrange(range);
	}

	let getdata = getdatabyselection(range);
	if (getdata.length == 0) {
		return;
	}

	let arr = [];
	for (let r = 0; r < getdata.length; r++) {
		let a = [];
		for (let c = 0; c < getdata[0].length; c++) {
			let value = "";
			if (getdata[r] != null && getdata[r][c] != null) {
				value = getdata[r][c];
				if (
					parseInt(value) != null &&
					getdata[r][c].ct != undefined &&
					getdata[r][c].ct.t == "n"
				) {
					if (type == "minus") {
						value.v = value.v - number;
					} else if (type == "multiply") {
						value.v = value.v * number;
					} else if (type == "divided") {
						value.v = numFormat(value.v / number, 4);
					} else if (type == "power") {
						value.v = Math.pow(value.v, number);
					} else if (type == "root") {
						if (number == 2) {
							value.v = numFormat(Math.sqrt(value.v), 4);
						} else if (number == 3 && Math.cbrt) {
							value.v = numFormat(Math.cbrt(value.v), 4);
						} else {
							value.v = numFormat(jfnqrt(value.v, number), 4);
						}
					} else if (type == "log") {
						value.v = numFormat(
							(Math.log(value.v) * 10000) /
								Math.log(Math.abs(number)),
							4
						);
					} else {
						value.v = value.v + number;
					}

					if (value.v == null) {
						value.m = "";
					} else {
						value.m = value.v.toString();
					}
				}
			}
			a.push(value);
		}
		arr.push(a);
	}

	editor.controlHandler(arr, range);

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 新增一个sheet，返回新增的工作表对象
 * @param {Object} options 可选参数
 * @param {Object} options.sheetObject 新增的工作表的数据；默认值为空对象
 * @param {Number} options.order 新增的工作表索引；默认值为最后一个索引位置
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetAdd(options = {}) {
	let lastOrder = Store.luckysheetfile.length - 1;
	let { sheetObject = {}, order = lastOrder, success } = { ...options };

	if (!isRealNum(order)) {
		return tooltip.info("Parameter is not a table index", "");
	}

	order = Number(order);

	let index = sheetmanage.generateRandomSheetIndex();
	// calcChain公式链里的index也要跟着变化
	if (sheetObject.calcChain && sheetObject.calcChain.length > 0) {
		sheetObject.calcChain.forEach((item) => {
			item.index = index;
		});
	}
	let sheetname = sheetmanage.generateRandomSheetName(
		Store.luckysheetfile,
		false
	);
	if (!!sheetObject.name) {
		let sameName = false;

		for (let i = 0; i < Store.luckysheetfile.length; i++) {
			if (Store.luckysheetfile[i].name == sheetObject.name) {
				sameName = true;
				break;
			}
		}

		if (!sameName) {
			sheetname = sheetObject.name;
		}
	}

	$("#luckysheet-sheet-container-c").append(
		replaceHtml(sheetHTML, {
			index: index,
			active: "",
			name: sheetname,
			style: "",
			colorset: "",
		})
	);

	let sheetconfig = {
		name: "",
		color: "",
		status: "0",
		order: "",
		index: "",
		celldata: [],
		row: Store.defaultrowNum,
		column: Store.defaultcolumnNum,
		config: {},
		pivotTable: null,
		isPivotTable: false,
	};
	sheetconfig = $.extend(true, sheetconfig, sheetObject);

	sheetconfig.index = index;
	sheetconfig.name = sheetname;
	sheetconfig.order = order;

	if (order <= 0) {
		let beforeIndex = Store.luckysheetfile[0].index;
		let beforeObj = $("#luckysheet-sheets-item" + beforeIndex);
		$("#luckysheet-sheets-item" + index).insertBefore(beforeObj);

		Store.luckysheetfile.splice(0, 0, sheetconfig);
	} else {
		if (order > Store.luckysheetfile.length) {
			order = Store.luckysheetfile.length;
		}

		let afterIndex = Store.luckysheetfile[order - 1].index;
		let afterObj = $("#luckysheet-sheets-item" + afterIndex);
		$("#luckysheet-sheets-item" + index).insertAfter(afterObj);

		Store.luckysheetfile.splice(order, 0, sheetconfig);
	}

	let orders = {};

	Store.luckysheetfile.forEach((item, i, arr) => {
		arr[i].order = i;
		orders[item.index.toString()] = i;
	});

	$("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass(
		"luckysheet-sheets-item-active"
	);
	$("#luckysheet-sheets-item" + index).addClass(
		"luckysheet-sheets-item-active"
	);
	$("#luckysheet-cell-main").append(
		'<div id="luckysheet-datavisual-selection-set-' +
			index +
			'" class="luckysheet-datavisual-selection-set"></div>'
	);
	cleargridelement(true);

	server.saveParam("sha", null, $.extend(true, {}, sheetconfig));
	server.saveParam("shr", null, orders);

	if (Store.clearjfundo) {
		Store.jfundo.length = 0;
		let redo = {};
		redo["type"] = "addSheet";
		redo["sheetconfig"] = $.extend(true, {}, sheetconfig);
		redo["index"] = index;
		redo["currentSheetIndex"] = Store.currentSheetIndex;
		Store.jfredo.push(redo);
	}

	sheetmanage.changeSheetExec(index, false, true);

	if (success && typeof success === "function") {
		success();
	}
	return sheetconfig;
}

/**
 * 删除指定下标的工作表，返回已删除的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetDelete(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	if (Store.luckysheetfile.length === 1) {
		return tooltip.info(locale().sheetconfig.noMoreSheet, "");
	}

	sheetmanage.deleteSheet(file.index);

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return file;
}

/**
 * 删除指定index的工作表，返回已删除的工作表对象
 * @param {Object} options 可选参数
 * @param {String} options.index 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteSheetByIndex(options = {}) {
	let { index, success } = { ...options };

	if (!index) {
		index = Store.luckysheetfile.find(
			(i) => i.order === Store.currentSheetIndex
		);
	}

	const order = Store.luckysheetfile.findIndex((i) => i.index == index);
	if (order == -1) {
		return tooltip.info("The index parameter is invalid.", "");
	}
	const file = Store.luckysheetfile[order];

	if (Store.luckysheetfile.length === 1) {
		return tooltip.info(locale().sheetconfig.noMoreSheet, "");
	}
	// 通过 index 找在 sheets 中的下标 删除是最准确的
	sheetmanage.deleteSheet(file.index);

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return file;
}

/**
 * 复制指定下标的工作表到指定下标位置
 * @param {Object} options 可选参数
 * @param {Number} options.targetOrder 新复制的工作表目标下标位置；默认值为当前工作表下标的下一个下标位置（递增）
 * @param {Number} options.order 被复制的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetCopy(options = {}) {
	let {
		targetOrder,
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	if (targetOrder == null) {
		targetOrder = order + 1;
	}

	if (!isRealNum(targetOrder)) {
		return tooltip.info("The targetOrder parameter is invalid.", "");
	}

	let copyindex = file.index;
	let index = sheetmanage.generateRandomSheetIndex();

	let copyjson = $.extend(true, {}, file);
	copyjson.order = Store.luckysheetfile.length;
	copyjson.index = index;
	copyjson.name = sheetmanage.generateCopySheetName(
		Store.luckysheetfile,
		copyjson.name
	);

	let colorset = "";
	if (copyjson.color != null) {
		colorset =
			'<div class="luckysheet-sheets-item-color" style=" position: absolute; width: 100%; height: 3px; bottom: 0px; left: 0px; background-color: ' +
			copyjson.color +
			';"></div>';
	}

	let afterObj = $("#luckysheet-sheets-item" + copyindex);
	if (isRealNum(targetOrder)) {
		afterObj = $(
			"#luckysheet-sheets-item" +
				Store.luckysheetfile[targetOrder - 1].index
		);
	}

	$("#luckysheet-sheet-container-c").append(
		replaceHtml(sheetHTML, {
			index: copyjson.index,
			active: "",
			name: copyjson.name,
			order: copyjson.order,
			style: "",
			colorset: colorset,
		})
	);
	$("#luckysheet-sheets-item" + copyjson.index).insertAfter(afterObj);
	Store.luckysheetfile.splice(targetOrder, 0, copyjson);

	$("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass(
		"luckysheet-sheets-item-active"
	);
	$("#luckysheet-sheets-item" + index).addClass(
		"luckysheet-sheets-item-active"
	);
	$("#luckysheet-cell-main").append(
		'<div id="luckysheet-datavisual-selection-set-' +
			index +
			'" class="luckysheet-datavisual-selection-set"></div>'
	);
	cleargridelement(true);

	server.saveParam("shc", index, {
		copyindex: copyindex,
		name: copyjson.name,
	});

	sheetmanage.changeSheetExec(index);
	sheetmanage.reOrderAllSheet();

	if (Store.clearjfundo) {
		Store.jfredo.push({
			type: "copySheet",
			copyindex: copyindex,
			index: copyjson.index,
			sheetIndex: copyjson.index,
		});
	} else if (Store.jfredo.length > 0) {
		let jfredostr = Store.jfredo[Store.jfredo.length - 1];

		if (jfredostr.type == "copySheet") {
			jfredostr.index = copyjson.index;
			jfredostr.sheetIndex = copyjson.index;
		}
	}

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return copyjson;
}

/**
 * 隐藏指定下标的工作表，返回被隐藏的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetHide(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	sheetmanage.setSheetHide(file.index);

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return file;
}

/**
 * 取消隐藏指定下标的工作表，返回被取消隐藏的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetShow(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	sheetmanage.setSheetShow(file.index);

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return file;
}

/**
 * 设置指定下标的工作表为当前工作表（激活态），即切换到指定的工作表，返回被激活的工作表对象
 * @param {Number} order 要激活的工作表下标
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetActive(order, options = {}) {
	if (
		order == null ||
		!isRealNum(order) ||
		Store.luckysheetfile[order] == null
	) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let file = Store.luckysheetfile[order];

	let { success } = { ...options };

	$("#luckysheet-sheet-area div.luckysheet-sheets-item").removeClass(
		"luckysheet-sheets-item-active"
	);
	$("#luckysheet-sheets-item" + file.index).addClass(
		"luckysheet-sheets-item-active"
	);

	sheetmanage.changeSheet(file.index);

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);
	server.multipleRangeShow();
	return file;
}

/**
 * 修改工作表名称
 * @param {String} name 工作表名称
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetName(name, options = {}) {
	if (getObjType(name) != "string" || name.toString().length == 0) {
		return tooltip.info("The name parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let oldtxt = file.name;
	file.name = name;

	$(
		"#luckysheet-sheets-item" + file.index + " .luckysheet-sheets-item-name"
	).text(name);

	server.saveParam("all", file.index, name, { k: "name" });

	if (Store.clearjfundo) {
		let redo = {};
		redo["type"] = "sheetName";
		redo["sheetIndex"] = file.index;

		redo["oldtxt"] = oldtxt;
		redo["txt"] = name;

		Store.jfundo.length = 0;
		Store.jfredo.push(redo);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 设置工作表名称处的颜色
 * @param {String} color 工作表颜色
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetColor(color, options = {}) {
	if (getObjType(color) != "string" || color.toString().length == 0) {
		return tooltip.info("The color parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let oldcolor = file.color;
	file.color = color;

	$("#luckysheet-sheets-item" + file.index)
		.find(".luckysheet-sheets-item-color")
		.remove();
	$("#luckysheet-sheets-item" + file.index).append(
		'<div class="luckysheet-sheets-item-color" style=" position: absolute; width: 100%; height: 3px; bottom: 0px; left: 0px; background-color: ' +
			color +
			';"></div>'
	);

	server.saveParam("all", file.index, color, { k: "color" });

	if (Store.clearjfundo) {
		let redo = {};
		redo["type"] = "sheetColor";
		redo["sheetIndex"] = file.index;

		redo["oldcolor"] = oldcolor;
		redo["color"] = color;

		Store.jfundo.length = 0;
		Store.jfredo.push(redo);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 指定工作表向左边或右边移动一个位置，或者指定索引，返回指定的工作表对象
 * @param {String | Number} type 工作表移动方向或者移动的目标索引
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表索引；默认值为当前工作表索引
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetMove(type, options = {}) {
	if (type != "left" && type != "right" && !isRealNum(type)) {
		return tooltip.info("Type parameter not available", "");
	}

	if (isRealNum(type)) {
		type = parseInt(type);
	}

	let curOrder = getSheetIndex(Store.currentSheetIndex);
	let { order = curOrder, success } = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("ncorrect worksheet index", "");
	}

	let sheetIndex = file.index;

	if (type == "left") {
		if (order == 0) {
			return;
		}

		let prevIndex = Store.luckysheetfile[order - 1].index;
		$("#luckysheet-sheets-item" + sheetIndex).insertBefore(
			$("#luckysheet-sheets-item" + prevIndex)
		);

		Store.luckysheetfile.splice(order, 1);
		Store.luckysheetfile.splice(order - 1, 0, file);
	} else if (type == "right") {
		if (order == Store.luckysheetfile.length - 1) {
			return;
		}

		let nextIndex = Store.luckysheetfile[order + 1].index;
		$("#luckysheet-sheets-item" + sheetIndex).insertAfter(
			$("#luckysheet-sheets-item" + nextIndex)
		);

		Store.luckysheetfile.splice(order, 1);
		Store.luckysheetfile.splice(order + 1, 0, file);
	} else {
		if (type < 0) {
			type = 0;
		}

		if (type > Store.luckysheetfile.length - 1) {
			type = Store.luckysheetfile.length - 1;
		}

		if (type == order) {
			return;
		}

		if (type < order) {
			let prevIndex = Store.luckysheetfile[type].index;
			$("#luckysheet-sheets-item" + sheetIndex).insertBefore(
				$("#luckysheet-sheets-item" + prevIndex)
			);
		} else {
			let nextIndex = Store.luckysheetfile[type].index;
			$("#luckysheet-sheets-item" + sheetIndex).insertAfter(
				$("#luckysheet-sheets-item" + nextIndex)
			);
		}

		Store.luckysheetfile.splice(order, 1);
		Store.luckysheetfile.splice(type, 0, file);
	}

	let orders = {};

	Store.luckysheetfile.forEach((item, i, arr) => {
		arr[i].order = i;
		orders[item.index.toString()] = i;
	});

	server.saveParam("shr", null, orders);

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 重新排序所有工作表的位置，指定工作表顺序的数组。
 * @param {Array} orderList 工作表顺序，设置工作表的index和order来指定位置
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 * @param {Boolean} options.broadcast 是否广播给其他客户端，默认为 true
 */
export function setSheetOrder(orderList, options = {}) {
	if (orderList == null || orderList.length == 0) {
		return tooltip.info("Type orderList not available", "");
	}

	let orderListMap = {};
	orderList.forEach((item) => {
		orderListMap[item.index.toString()] = item.order;
	});

	Store.luckysheetfile.sort((x, y) => {
		let order_x = orderListMap[x.index.toString()];
		let order_y = orderListMap[y.index.toString()];

		if (order_x != null && order_y != null) {
			return order_x - order_y;
		} else if (order_x != null) {
			return -1;
		} else if (order_y != null) {
			return 1;
		} else {
			return 1;
		}
	});

	let orders = {};

	Store.luckysheetfile.forEach((item, i, arr) => {
		arr[i].order = i;
		orders[item.index.toString()] = i;

		if (i > 0) {
			let preIndex = arr[i - 1].index;
			$("#luckysheet-sheets-item" + item.index).insertAfter(
				$("#luckysheet-sheets-item" + preIndex)
			);
		}
	});

	let { success } = { ...options };

	if (success && typeof success === "function") {
		success();
	}

	if (options.broadcast && options.broadcast === false) return;
	console.log("==> ", options.broadcast && options.broadcast === false);
	server.saveParam("shr", null, orders);
}

/**
 * 设置工作表缩放比例
 * @param {Number} zoom 工作表缩放比例，值范围为0.1 ~ 4；
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setSheetZoom(zoom, options = {}) {
	if (!isRealNum(zoom) || zoom < 0.1 || zoom > 4) {
		return tooltip.info("The zoom parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	file["zoomRatio"] = zoom;

	server.saveParam("all", file.index, zoom, { k: "zoomRatio" });

	if (file.index == Store.currentSheetIndex) {
		Store.zoomRatio = zoom;
		// 图片
		let currentSheet = sheetmanage.getSheetByIndex();
		imageCtrl.images = currentSheet.images;
		imageCtrl.allImagesShow();
		imageCtrl.init();

		zoomNumberDomBind();
		zoomRefreshView();
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 显示指定下标工作表的网格线，返回操作的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 需要显示网格线的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function showGridLines(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	file.showGridLines = true;

	if (file.index == Store.currentSheetIndex) {
		Store.showGridLines = true;

		setTimeout(function () {
			luckysheetrefreshgrid();
		}, 1);
	}

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return file;
}

/**
 * 隐藏指定下标工作表的网格线，返回操作的工作表对象
 * @param {Object} options 可选参数
 * @param {Number} options.order 需要显示网格线的工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function hideGridLines(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	file.showGridLines = false;

	if (file.index == Store.currentSheetIndex) {
		Store.showGridLines = false;

		setTimeout(function () {
			luckysheetrefreshgrid();
		}, 1);
	}

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return file;
}

/**
 * 刷新canvas
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function refresh(options = {}) {
	// luckysheetrefreshgrid();
	jfrefreshgrid();

	let { success } = { ...options };

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 滚动当前工作表位置
 * @param {Object} options 可选参数
 * @param {Number} options.scrollLeft 横向滚动值
 * @param {Number} options.scrollTop 纵向滚动值
 * @param {Number} options.targetRow 纵向滚动到指定的行号
 * @param {Number} options.targetColumn 横向滚动到指定的列号
 * @param {Function} options.success 操作结束的回调函数
 */
export function scroll(options = {}) {
	let { scrollLeft, scrollTop, targetRow, targetColumn, success } = {
		...options,
	};

	if (scrollLeft != null) {
		if (!isRealNum(scrollLeft)) {
			return tooltip.info("The scrollLeft parameter is invalid.", "");
		}

		$("#luckysheet-scrollbar-x").scrollLeft(scrollLeft);
	} else if (targetColumn != null) {
		if (!isRealNum(targetColumn)) {
			return tooltip.info("The targetColumn parameter is invalid.", "");
		}

		let col = Store.visibledatacolumn[targetColumn],
			col_pre =
				targetColumn <= 0
					? 0
					: Store.visibledatacolumn[targetColumn - 1];

		$("#luckysheet-scrollbar-x").scrollLeft(col_pre);
	}

	if (scrollTop != null) {
		if (!isRealNum(scrollTop)) {
			return tooltip.info("The scrollTop parameter is invalid.", "");
		}

		$("#luckysheet-scrollbar-y").scrollTop(scrollTop);
	} else if (targetRow != null) {
		if (!isRealNum(targetRow)) {
			return tooltip.info("The targetRow parameter is invalid.", "");
		}

		let row = Store.visibledatarow[targetRow],
			row_pre = targetRow <= 0 ? 0 : Store.visibledatarow[targetRow - 1];

		$("#luckysheet-scrollbar-y").scrollTop(row_pre);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 根据窗口大小自动resize画布
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function resize(options = {}) {
	luckysheetsizeauto();

	let { success } = { ...options };

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 返回指定选区截图后生成的base64格式的图片
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围，只能为单个选区；默认为当前选区
 */
export function getScreenshot(options = {}) {
	let {
		range = Store.luckysheet_select_save[
			Store.luckysheet_select_save.length - 1
		],
	} = { ...options };

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = {
			row: cellrange.row,
			column: cellrange.column,
		};
	}

	if (
		getObjType(range) != "object" ||
		range.row == null ||
		range.column == null
	) {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let str = range.row[0],
		edr = range.row[1],
		stc = range.column[0],
		edc = range.column[1];

	let has_PartMC = hasPartMC(Store.config, str, edr, stc, edc);

	if (has_PartMC) {
		return tooltip.info(
			"Cannot perform this operation on partially merged cells",
			""
		);
	}

	let visibledatarow = Store.visibledatarow;
	let visibledatacolumn = Store.visibledatacolumn;

	let scrollHeight, rh_height;
	if (str - 1 < 0) {
		scrollHeight = 0;
		rh_height = visibledatarow[edr];
	} else {
		scrollHeight = visibledatarow[str - 1];
		rh_height = visibledatarow[edr] - visibledatarow[str - 1];
	}

	let scrollWidth, ch_width;
	if (stc - 1 < 0) {
		scrollWidth = 0;
		ch_width = visibledatacolumn[edc];
	} else {
		scrollWidth = visibledatacolumn[stc - 1];
		ch_width = visibledatacolumn[edc] - visibledatacolumn[stc - 1];
	}

	let newCanvas = $("<canvas>")
		.attr({
			width: Math.ceil(ch_width * Store.devicePixelRatio),
			height: Math.ceil(rh_height * Store.devicePixelRatio),
		})
		.css({ width: ch_width, height: rh_height });

	luckysheetDrawMain(
		scrollWidth,
		scrollHeight,
		ch_width,
		rh_height,
		1,
		1,
		null,
		null,
		newCanvas
	);
	let ctx_newCanvas = newCanvas.get(0).getContext("2d");

	//补上 左边框和上边框
	ctx_newCanvas.beginPath();
	ctx_newCanvas.moveTo(0, 0);
	ctx_newCanvas.lineTo(0, Store.devicePixelRatio * rh_height);
	ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
	ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
	ctx_newCanvas.stroke();
	ctx_newCanvas.closePath();

	ctx_newCanvas.beginPath();
	ctx_newCanvas.moveTo(0, 0);
	ctx_newCanvas.lineTo(Store.devicePixelRatio * ch_width, 0);
	ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
	ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
	ctx_newCanvas.stroke();
	ctx_newCanvas.closePath();

	let url = newCanvas.get(0).toDataURL("image/png");

	return url;
}

/**
 * 设置工作簿名称
 * @param {String} name 工作簿名称
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function setWorkbookName(name, options = {}) {
	if (name == null || name.toString().length == 0) {
		return tooltip.info("The name parameter is invalid.", "");
	}

	$("#luckysheet_info_detail_input").val(name);

	let { success } = { ...options };

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 获取工作簿名称
 * @param   {Object}    options         可选参数
 * @param   {Function}  options.success 操作结束的回调函数
 * @returns {String}    返回工作簿名称，如果读取失败则返回空字符串并弹窗提示
 */
export function getWorkbookName(options = {}) {
	let name = "";
	let element = $("#luckysheet_info_detail_input");

	if (element.length == 0) {
		tooltip.info("Failed to get workbook name, label loading failed!");
		return name;
	}

	name = $.trim(element.val());

	let { success } = { ...options };

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return name;
}

/**
 * 撤销当前操作，返回刚刚撤销的操作对象
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function undo(options = {}) {
	let ctr = $.extend(true, {}, Store.jfredo[Store.jfredo.length - 1]);

	controlHistory.redo(new Event("custom"));
	luckysheetactiveCell();

	let { success } = { ...options };

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return ctr;
}

/**
 * 重做当前操作，返回刚刚重做的操作对象
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function redo(options = {}) {
	let ctr = $.extend(true, {}, Store.jfundo[Store.jfundo.length - 1]);

	controlHistory.undo(new Event("custom"));
	luckysheetactiveCell();

	let { success } = { ...options };

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return ctr;
}

/**
 * 返回所有工作表配置
 */
export function getAllSheets() {
	let data = $.extend(true, [], Store.luckysheetfile);

	data.forEach((item, index, arr) => {
		if (item.data != null && item.data.length > 0) {
			item.celldata = sheetmanage.getGridData(item.data);
		}

		delete item.load;
		delete item.freezen;
	});

	return data;
}

/**
 * 根据index获取sheet页配置
 *
 * @param {Object} options 可选参数
 * @param {String} options.index 工作表index
 * @param {Number} options.order 工作表order
 * @param {String} options.name 工作表name
 */
export function getSheet(options = {}) {
	let { index, order, name } = { ...options };

	if (index != null) {
		return sheetmanage.getSheetByIndex(index);
	} else if (order != null) {
		return Store.luckysheetfile[order];
	} else if (name != null) {
		return sheetmanage.getSheetByName(name);
	}

	return sheetmanage.getSheetByIndex();
}

/**
 * 快捷返回指定工作表的数据
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 */
export function getSheetData(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex) } = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let data = $.extend(true, [], file.data);

	if (data == null || data.length == 0) {
		data = $.extend(true, [], sheetmanage.buildGridData(file));
	}

	return data;
}

/**
 * 快捷返回指定工作表的config配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 */
export function getConfig(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex) } = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let config = $.extend(true, {}, file.config);

	return config;
}

/**
 * 快捷设置指定工作表config配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setConfig(cfg, options = {}) {
	if (getObjType(cfg) != "object") {
		return tooltip.info("The cfg parameter is invalid.", "");
	}

	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	file.config = cfg;

	if (file.index == Store.currentSheetIndex) {
		Store.config = cfg;

		if (
			"rowhidden" in cfg ||
			"colhidden" in cfg ||
			"rowlen" in cfg ||
			"columnlen" in cfg
		) {
			jfrefreshgrid_rhcw(Store.flowdata.length, Store.flowdata[0].length);
		}

		setTimeout(function () {
			luckysheetrefreshgrid();
		}, 1);
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 返回所有表格数据结构的一维数组luckysheetfile
 */
export function getLuckysheetfile() {
	return getluckysheetfile();
}

/**
 * 指定工作表范围设置数据验证功能，并设置参数
 * @param {Object} optionItem 数据验证的配置信息
 * @param {String} optionItem.type 类型
 * @param {String | Null} optionItem.type2 条件类型
 * @param {String | Number} optionItem.value1 条件值1
 * @param {String | Number} optionItem.value2 条件值2
 * @param {Boolean} optionItem.checked 选中状态
 * @param {Boolean} optionItem.remote 自动远程获取选项
 * @param {Boolean} optionItem.prohibitInput 输入数据无效时禁止输入
 * @param {Boolean} optionItem.hintShow 选中单元格时显示提示语
 * @param {String} optionItem.hintText 提示语文本
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围；默认为当前选区
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function setDataVerification(optionItem, options = {}) {
	if (getObjType(optionItem) != "object") {
		return tooltip.info("The optionItem parameter is invalid.", "");
	}

	let {
		type,
		type2 = null,
		value1 = "",
		value2 = "",
		remote = false,
		prohibitInput = false,
		hintShow = false,
		hintText = "",
		checked = false,
	} = { ...optionItem };

	let typeValues = [
		"dropdown",
		"checkbox",
		"number",
		"number_integer",
		"number_decimal",
		"text_content",
		"text_length",
		"date",
		"validity",
	];
	let type2Values_1 = ["bw", "nb", "eq", "ne", "gt", "lt", "gte", "lte"];
	let type2Values_2 = ["include", "exclude", "equal"];
	let type2Values_3 = ["bw", "nb", "eq", "ne", "bf", "nbf", "af", "naf"];
	let type2Values_4 = ["card", "phone"];

	if (!typeValues.includes(type)) {
		return tooltip.info("The optionItem.type parameter is invalid.", "");
	}

	let dvText = locale().dataVerification;

	if (type == "dropdown") {
		if (value1.length == 0) {
			tooltip.info(
				'<i class="fa fa-exclamation-triangle"></i>',
				dvText.tooltipInfo1
			);
			return;
		}
	} else if (type == "checkbox") {
		if (value1.length == 0 || value2.length == 0) {
			tooltip.info(
				'<i class="fa fa-exclamation-triangle"></i>',
				dvText.tooltipInfo2
			);
			return;
		}
	} else if (
		type == "number" ||
		type == "number_integer" ||
		type == "number_decimal"
	) {
		if (!type2Values_1.includes(type2)) {
			return tooltip.info(
				"The optionItem.type2 parameter is invalid.",
				""
			);
		}

		if (!isRealNum(value1)) {
			tooltip.info(
				'<i class="fa fa-exclamation-triangle"></i>',
				dvText.tooltipInfo3
			);
			return;
		}

		if (type2 == "bw" || type2 == "nb") {
			if (!isRealNum(value2)) {
				tooltip.info(
					'<i class="fa fa-exclamation-triangle"></i>',
					dvText.tooltipInfo3
				);
				return;
			}

			if (Number(value2) < Number(value1)) {
				tooltip.info(
					'<i class="fa fa-exclamation-triangle"></i>',
					dvText.tooltipInfo4
				);
				return;
			}
		}
	} else if (type == "text_content") {
		if (!type2Values_2.includes(type2)) {
			return tooltip.info(
				"The optionItem.type2 parameter is invalid.",
				""
			);
		}

		if (value1.length == 0) {
			tooltip.info(
				'<i class="fa fa-exclamation-triangle"></i>',
				dvText.tooltipInfo5
			);
			return;
		}
	} else if (type == "text_length") {
		if (!type2Values_1.includes(type2)) {
			return tooltip.info(
				"The optionItem.type2 parameter is invalid.",
				""
			);
		}

		if (!isRealNum(value1)) {
			tooltip.info(
				'<i class="fa fa-exclamation-triangle"></i>',
				dvText.tooltipInfo3
			);
			return;
		}

		if (type2 == "bw" || type2 == "nb") {
			if (!isRealNum(value2)) {
				tooltip.info(
					'<i class="fa fa-exclamation-triangle"></i>',
					dvText.tooltipInfo3
				);
				return;
			}

			if (Number(value2) < Number(value1)) {
				tooltip.info(
					'<i class="fa fa-exclamation-triangle"></i>',
					dvText.tooltipInfo4
				);
				return;
			}
		}
	} else if (type == "date") {
		if (!type2Values_3.includes(type2)) {
			return tooltip.info(
				"The optionItem.type2 parameter is invalid.",
				""
			);
		}

		if (!isdatetime(value1)) {
			tooltip.info(
				'<i class="fa fa-exclamation-triangle"></i>',
				dvText.tooltipInfo6
			);
			return;
		}

		if (type2 == "bw" || type2 == "nb") {
			if (!isdatetime(value2)) {
				tooltip.info(
					'<i class="fa fa-exclamation-triangle"></i>',
					dvText.tooltipInfo6
				);
				return;
			}

			if (diff(value1, value2) > 0) {
				tooltip.info(
					'<i class="fa fa-exclamation-triangle"></i>',
					dvText.tooltipInfo7
				);
				return;
			}
		}
	} else if (type == "validity") {
		if (!type2Values_4.includes(type2)) {
			return tooltip.info(
				"The optionItem.type2 parameter is invalid.",
				""
			);
		}
	}

	if (getObjType(remote) != "boolean") {
		return tooltip.info("The optionItem.remote parameter is invalid.", "");
	}

	if (getObjType(prohibitInput) != "boolean") {
		return tooltip.info(
			"The optionItem.prohibitInput parameter is invalid.",
			""
		);
	}

	if (getObjType(hintShow) != "boolean") {
		return tooltip.info(
			"The optionItem.hintShow parameter is invalid.",
			""
		);
	}

	let {
		range = Store.luckysheet_select_save[
			Store.luckysheet_select_save.length - 1
		],
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = {
			row: cellrange.row,
			column: cellrange.column,
		};
	}

	if (
		getObjType(range) != "object" ||
		range.row == null ||
		range.column == null
	) {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let item = {
		type: type,
		type2: type2,
		value1: value1,
		value2: value2,
		checked: checked,
		remote: remote,
		prohibitInput: prohibitInput,
		hintShow: hintShow,
		hintText: hintText,
	};

	let currentDataVerification = $.extend(true, {}, file.dataVerification);

	let data = $.extend(true, [], file.data);
	if (data.length == 0) {
		data = sheetmanage.buildGridData(file);
	}

	let str = range.row[0],
		edr = range.row[1],
		stc = range.column[0],
		edc = range.column[1];

	for (let r = str; r <= edr; r++) {
		for (let c = stc; c <= edc; c++) {
			currentDataVerification[r + "_" + c] = item;

			if (type == "checkbox") {
				item.checked
					? setcellvalue(r, c, data, item.value1)
					: setcellvalue(r, c, data, item.value2);
			}
		}
	}

	if (file.index == Store.currentSheetIndex) {
		let historyDataVerification = $.extend(true, {}, file.dataVerification);

		if (type == "checkbox") {
			dataVerificationCtrl.refOfCheckbox(
				historyDataVerification,
				currentDataVerification,
				Store.currentSheetIndex,
				data,
				range
			);
		} else {
			dataVerificationCtrl.ref(
				historyDataVerification,
				currentDataVerification,
				Store.currentSheetIndex
			);
		}
	} else {
		file.dataVerification = currentDataVerification;
		file.data = data;
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 指定工作表范围删除数据验证功能
 * @param {Object} options 可选参数
 * @param {Array | Object | String} options.range 选区范围；默认为当前选区
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteDataVerification(options = {}) {
	let {
		range = Store.luckysheet_select_save[
			Store.luckysheet_select_save.length - 1
		],
		order = getSheetIndex(Store.currentSheetIndex),
		success,
	} = { ...options };

	if (getObjType(range) == "string") {
		if (!formula.iscelldata(range)) {
			return tooltip.info("The range parameter is invalid.", "");
		}

		let cellrange = formula.getcellrange(range);
		range = {
			row: cellrange.row,
			column: cellrange.column,
		};
	}

	if (
		getObjType(range) != "object" ||
		range.row == null ||
		range.column == null
	) {
		return tooltip.info("The range parameter is invalid.", "");
	}

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let currentDataVerification = $.extend(true, {}, file.dataVerification);

	let str = range.row[0],
		edr = range.row[1],
		stc = range.column[0],
		edc = range.column[1];

	for (let r = str; r <= edr; r++) {
		for (let c = stc; c <= edc; c++) {
			delete currentDataVerification[r + "_" + c];
		}
	}

	if (file.index == Store.currentSheetIndex) {
		let historyDataVerification = $.extend(true, {}, file.dataVerification);
		dataVerificationCtrl.ref(
			historyDataVerification,
			currentDataVerification,
			Store.currentSheetIndex
		);
	} else {
		file.dataVerification = currentDataVerification;
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 在指定的工作表中指定单元格位置插入图片
 * @param {String} src 图片src
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Number} options.rowIndex 要插入图片的单元格行下标；默认为0
 * @param {Number} options.colIndex 要插入图片的单元格列下标；默认为0
 * @param {Function} options.success 操作结束的回调函数
 */
export function insertImage(src, options = {}) {
	let {
		order = getSheetIndex(Store.currentSheetIndex),
		rowIndex,
		colIndex,
		success,
	} = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	if (file.index == Store.currentSheetIndex) {
		let last =
			Store.luckysheet_select_save[
				Store.luckysheet_select_save.length - 1
			];

		if (rowIndex == null) {
			rowIndex = last.row_focus || 0;
		}

		if (rowIndex < 0) {
			rowIndex = 0;
		}

		if (rowIndex > Store.visibledatarow.length) {
			rowIndex = Store.visibledatarow.length;
		}

		if (colIndex == null) {
			colIndex = last.column_focus || 0;
		}

		if (colIndex < 0) {
			colIndex = 0;
		}

		if (colIndex > Store.visibledatacolumn.length) {
			colIndex = Store.visibledatacolumn.length;
		}

		let left = colIndex == 0 ? 0 : Store.visibledatacolumn[colIndex - 1];
		let top = rowIndex == 0 ? 0 : Store.visibledatarow[rowIndex - 1];

		let image = new Image();
		image.onload = function () {
			let width = image.width,
				height = image.height;

			let img = {
				src: src,
				left: left,
				top: top,
				originWidth: width,
				originHeight: height,
			};

			imageCtrl.addImgItem(img);

			if (success && typeof success === "function") {
				success();
			}
		};
		image.src = src;
	} else {
		let images = file.images || {};
		let config = file.config;
		let zoomRatio = file.zoomRatio || 1;

		let rowheight = file.row;
		let visibledatarow = file.visibledatarow || [];
		if (visibledatarow.length === 0) {
			let rh_height = 0;

			for (let r = 0; r < rowheight; r++) {
				let rowlen = Store.defaultrowlen;

				if (config["rowlen"] != null && config["rowlen"][r] != null) {
					rowlen = config["rowlen"][r];
				}

				if (
					config["rowhidden"] != null &&
					config["rowhidden"][r] != null
				) {
					visibledatarow.push(rh_height);
					continue;
				}

				rh_height += Math.round((rowlen + 1) * zoomRatio);

				visibledatarow.push(rh_height); //行的临时长度分布
			}
		}

		let colwidth = file.column;
		let visibledatacolumn = file.visibledatacolumn || [];
		if (visibledatacolumn.length === 0) {
			let ch_width = 0;

			for (let c = 0; c < colwidth; c++) {
				let firstcolumnlen = Store.defaultcollen;

				if (
					config["columnlen"] != null &&
					config["columnlen"][c] != null
				) {
					firstcolumnlen = config["columnlen"][c];
				}

				if (
					config["colhidden"] != null &&
					config["colhidden"][c] != null
				) {
					visibledatacolumn.push(ch_width);
					continue;
				}

				ch_width += Math.round((firstcolumnlen + 1) * zoomRatio);

				visibledatacolumn.push(ch_width); //列的临时长度分布
			}
		}

		if (rowIndex == null) {
			rowIndex = 0;
		}

		if (rowIndex < 0) {
			rowIndex = 0;
		}

		if (rowIndex > visibledatarow.length) {
			rowIndex = visibledatarow.length;
		}

		if (colIndex == null) {
			colIndex = 0;
		}

		if (colIndex < 0) {
			colIndex = 0;
		}

		if (colIndex > visibledatacolumn.length) {
			colIndex = visibledatacolumn.length;
		}

		let left = colIndex == 0 ? 0 : visibledatacolumn[colIndex - 1];
		let top = rowIndex == 0 ? 0 : visibledatarow[rowIndex - 1];

		let image = new Image();
		image.onload = function () {
			let img = {
				src: src,
				left: left,
				top: top,
				originWidth: image.width,
				originHeight: image.height,
			};

			let width, height;
			let max = 400;

			if (img.originHeight < img.originWidth) {
				height = Math.round(img.originHeight * (max / img.originWidth));
				width = max;
			} else {
				width = Math.round(img.originWidth * (max / img.originHeight));
				height = max;
			}

			let imgItem = $.extend(true, {}, imageCtrl.imgItem);
			imgItem.src = img.src;
			imgItem.originWidth = img.originWidth;
			imgItem.originHeight = img.originHeight;
			imgItem.default.width = width;
			imgItem.default.height = height;
			imgItem.default.left = img.left;
			imgItem.default.top = img.top;
			imgItem.crop.width = width;
			imgItem.crop.height = height;

			let id = imageCtrl.generateRandomId();
			images[id] = imgItem;

			file.images = images;

			if (success && typeof success === "function") {
				success();
			}
		};
		image.src = src;
	}
}

/**
 * 删除指定工作表中的图片
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {String | Array} options.idList 要删除图片的id集合，也可为字符串`"all"`，all为所有的字符串；默认为`"all"`
 * @param {Function} options.success 操作结束的回调函数
 */
export function deleteImage(options = {}) {
	let {
		order = getSheetIndex(Store.currentSheetIndex),
		idList = "all",
		success,
	} = { ...options };

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	let images = file.images;

	if (images == null) {
		return tooltip.info("The worksheet has no pictures to delete.", "");
	}

	if (idList != "all" && getObjType(idList) != "array") {
		return tooltip.info("The idList parameter is invalid.", "");
	}

	if (getObjType(idList) == "array") {
		idList.forEach((item) => {
			delete images[item];
		});
	} else {
		images = null;
	}

	file.images = images;

	if (file.index == Store.currentSheetIndex) {
		if (
			imageCtrl.currentImgId != null &&
			(idList == "all" || idList.includes(imageCtrl.currentImgId))
		) {
			$("#luckysheet-modal-dialog-activeImage").hide();
			$("#luckysheet-modal-dialog-cropping").hide();
			$("#luckysheet-modal-dialog-slider-imageCtrl").hide();
		}

		imageCtrl.images = images;
		imageCtrl.allImagesShow();
		imageCtrl.init();
	}

	if (success && typeof success === "function") {
		success();
	}
}

/**
 * 获取指定工作表的图片配置
 * @param {Object} options 可选参数
 * @param {Number} options.order 工作表下标；默认值为当前工作表下标
 * @param {Function} options.success 操作结束的回调函数
 */
export function getImageOption(options = {}) {
	let { order = getSheetIndex(Store.currentSheetIndex), success } = {
		...options,
	};

	let file = Store.luckysheetfile[order];

	if (file == null) {
		return tooltip.info("The order parameter is invalid.", "");
	}

	setTimeout(function () {
		if (success && typeof success === "function") {
			success();
		}
	}, 1);

	return file.images;
}

/**
 * data => celldata ，data二维数组数据转化成 {r, c, v}格式一维数组
 *
 * @param {Array} data 二维数组数据
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function transToCellData(data, options = {}) {
	let { success } = { ...options };

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 0);

	return sheetmanage.getGridData(data);
}

/**
 * celldata => data ，celldata一维数组数据转化成表格所需二维数组
 *
 * @param {Array} celldata 二维数组数据
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 */
export function transToData(celldata, options = {}) {
	let { success } = { ...options };

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	}, 0);

	return sheetmanage.buildGridData({
		celldata: celldata,
	});
}

/**
 * 导出的json字符串可以直接当作`luckysheet.create(options)`初始化工作簿时的参数`options`使用
 *
 */
export function toJson() {
	const toJsonOptions = Store.toJsonOptions;

	// Workbook name
	toJsonOptions.title = $("#luckysheet_info_detail_input").val();

	toJsonOptions.data = getAllSheets();

	// row and column
	getluckysheetfile().forEach((file, index) => {
		if (file.data == undefined) {
			return;
		}
		toJsonOptions.data[index].row =
			getObjType(file.data) === "array" ? file.data.length : 0;
		toJsonOptions.data[index].column =
			getObjType(file.data[0]) === "array" ? file.data[0].length : 0;
	});

	return toJsonOptions;
}

/**
 * 传入目标语言，切换到对应的语言界面
 * @param {String} lang 可选参数；暂支持`"zh"`、`"en"`、`"es"`；默认为`"zh"`；
 */
export function changLang(lang = "zh") {
	if (!["zh", "en", "es"].includes(lang)) {
		return tooltip.info("The lang parameter is invalid.", "");
	}

	let options = toJson();
	options.lang = lang;
	luckysheet.create(options);
}

/**
 * 关闭websocket连接
 */
export function closeWebsocket() {
	if (server.websocket == null) {
		return;
	}
	server.websocket.close(1000);
}

/**
 * 根据范围字符串转换为range数组
 * @param {String} txt 范围字符串
 */
export function getRangeByTxt(txt) {
	// 默认取当前第一个范围
	if (txt == null) {
		return {
			column: Store.luckysheet_select_save[
				Store.luckysheet_select_save.length - 1
			].column,
			row: Store.luckysheet_select_save[
				Store.luckysheet_select_save.length - 1
			].row,
		};
	}

	const range = conditionformat.getRangeByTxt(txt);

	return {
		column: range[0].column,
		row: range[0].row,
	};
}

/**
 * 根据范围数组转换为范围字符串
 * @param {Object | Array} range 范围数组
 */
export function getTxtByRange(range = Store.luckysheet_select_save) {
	// 单个范围
	if (getObjType(range) === "object") {
		range = [range];
	}
	return conditionformat.getTxtByRange(range);
}

/**
 * 初始化分页器
 * @param {Object} config 分页器配置
 * @param {Number} config.pageIndex 当前的页码
 * @param {Number} config.pageSize 每页显示多少条数据
 * @param {Array} config.selectOption 选择每页的条数
 * @param {Number} config.total 总条数
 */
export function pagerInit(config) {
	const { prevPage, nextPage, total } = locale().button;
	$("#luckysheet-bottom-pager").remove();
	$("#luckysheet-sheet-content").after(
		'<div id="luckysheet-bottom-pager" style="font-size: 14px; margin-left: 10px; display: inline-block;"></div>'
	);
	$("#luckysheet-bottom-pager").sPage({
		page: config.pageIndex, //当前页码，必填
		total: config.total, //数据总条数，必填
		selectOption: config.selectOption, // 选择每页的行数，
		pageSize: config.pageSize, //每页显示多少条数据，默认10条
		showTotal: config.showTotal, // 是否显示总数，默认关闭：false
		showSkip: config.showSkip, //是否显示跳页，默认关闭：false
		showPN: config.showPN, //是否显示上下翻页，默认开启：true
		prevPage: config.prevPage || prevPage, //上翻页文字描述，默认"上一页"
		nextPage: config.nextPage || nextPage, //下翻页文字描述，默认"下一页"
		totalTxt: config.totalTxt || total + config.total, // 数据总条数文字描述，{total}为占位符，默认"总共：{total}"
		backFun: function (page) {
			page.pageIndex = page.page;
			if (!method.createHookFunction("onTogglePager", page)) {
				return;
			}
		},
	});
}

/**
 * 刷新公式
 * @param {Function} success 回调函数
 */
export function refreshFormula(success) {
	formula.execFunctionGroupForce(true);
	luckysheetrefreshgrid();
	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	});
}

/**
 * 更新sheet数据
 * @param {Array} data 工作簿配置，可以包含多个表
 * @param {Object} options 可选参数
 * @param {Function} options.success 操作结束的回调函数
 *
 */
export function updataSheet(options = {}) {
	let { data, success } = options;
	let files = Store.luckysheetfile;
	for (let i = 0; i < data.length; i++) {
		for (let j = 0; j < files.length; j++) {
			if (files[j].index === data[i].index) {
				files[j] = data[i];
			}
		}
	}
	let file = files[sheetmanage.getSheetIndex(Store.currentSheetIndex)],
		sheetData = sheetmanage.buildGridData(file);
	file.data = sheetData;

	if (!!file.isPivotTable) {
		Store.luckysheetcurrentisPivotTable = true;
		if (!isPivotInitial) {
			pivotTable.changePivotTable(index);
		}
	} else {
		Store.luckysheetcurrentisPivotTable = false;
		$("#luckysheet-modal-dialog-slider-pivot").hide();
		luckysheetsizeauto(false);
	}
	sheetmanage.mergeCalculation(file["index"]);
	sheetmanage.setSheetParam();
	setTimeout(function () {
		sheetmanage.showSheet();
		sheetmanage.restoreCache();
		formula.execFunctionGroupForce(
			luckysheetConfigsetting.forceCalculation
		);
		sheetmanage.restoreSheetAll(Store.currentSheetIndex);
		luckysheetrefreshgrid();
		if (success && typeof success === "function") {
			success();
		}
	}, 1);
	server.saveParam("shs", null, Store.currentSheetIndex);
}

/**
 * 刷新状态栏的状态
 * @param {Array}  data             操作数据
 * @param {Number} r                指定的行
 * @param {Number} c                指定的列
 * @param {Function} success        回调函数
 */
export function refreshMenuButtonFocus(data, r, c, success) {
	data = data || Store.flowdata;
	if (r == null && c == null) {
		/* 获取选取范围 */
		let last =
			Store.luckysheet_select_save[
				Store.luckysheet_select_save.length - 1
			];

		r = last.row_focus || last.row[0];
		c = last.column_focus || last.column[0];
	}

	menuButton.menuButtonFocus(data, r, c);

	setTimeout(() => {
		if (success && typeof success === "function") {
			success();
		}
	});
}

/**
 * 检查选区内所有cell指定类型的状态是否满足条件（主要是粗体、斜体、删除线和下划线等等）
 * @param {String}  type            类型
 * @param {String}  status          目标状态值
 */
export function checkTheStatusOfTheSelectedCells(type, status) {
	/* 获取选区内所有的单元格-扁平后的处理 */
	let cells = getRangeWithFlatten();

	let flag = cells.every(({ r, c }) => {
		let cell = Store.flowdata[r][c];
		if (cell == null) {
			return false;
		}
		return cell[type] == status;
	});

	return flag;
}

/**
 * 创建统计图 - 需要传入生成统计图的行列数据
 * ?? 这里的参数可能还需要优化哈，不能笼统的传入 行列 应该区分 rowArr colArr contentArr
 * @param {String}  sheetIndex            sheet index
 * @param {Array}   row                   数据行
 * @param {Array}   column                数据列
 * @param {number}  width                 图表宽度
 * @param {number}  height                图表高度
 * @param {number}  top                   图表距离顶部距离
 * @param {number}  left                  图表距离左侧距离
 */
export function createChart(sheetIndex, row, column, width, height, top, left) {
	// 生成chartID
	let chart_id = generateRandomKey("chart");

	// 获取统计图数据
	let chartData = getdatabyselection({ row, column }, sheetIndex);

	var rangeTxt = getRangetxt(
		sheetIndex,
		{ row, column },
		chartInfo.currentSheetIndex
	);

	console.dir(chartData);

	// 调用 controll
	return chartController.createChart(
		sheetIndex,
		chart_id,
		chartData,
		[{ row, column }],
		rangeTxt,
		width,
		height,
		top,
		left
	);
}

/**
 * 更新统计图 - 需要传入生成统计图的行列数据
 * @param {String}  chart_id            统计图ID
 * @param {Object}  options             更新的配置项：宽高 位置 数据等
 */
export function updateChart(data) {
	return chartController.updateChart(data);
}

/**
 * 更新统计图 - 需要传入生成统计图的行列数据
 * @param {String}  sheetIndex          sheet id
 * @param {String}  chart_id            统计图ID
 * @param {Object}  chartOptions        传入的统计图配置项
 * @param {number}  width               图表宽度
 * @param {number}  height              图表高度
 * @param {number}  top                 图表距离顶部距离
 * @param {number}  left                图表距离左侧距离
 */
export function renderChart(data) {
	return chartController.renderChart(data);
}

/**
 * 删除统计图 - 需要传入删除的统计图ID
 * @param {String}  chart_id            统计图ID
 */
export function deleteChart(chart_id) {
	return chartController.deleteChart(chart_id);
}

/** 增强 Lucky sheet 图表能力 - 拓展 VChart 插件实现 */

/**
 * 增强 Lucky sheet 图表能力 - 拓展 VChart 插件实现
 * 创建统计图 - 需要传入生成统计图的行列数据
 * @param {String}  sheetIndex            sheet index
 * @param {Array}   row                   数据行
 * @param {Array}   column                数据列
 * @param {number}  width                 图表宽度
 * @param {number}  height                图表高度
 * @param {number}  top                   图表距离顶部距离
 * @param {number}  left                  图表距离左侧距离
 */
export function createVChart(
	sheetIndex,
	row,
	column,
	width,
	height,
	top,
	left
) {
	// 生成chartID
	let chart_id = generateRandomKey("chart");

	// 获取统计图数据
	let chartData = getdatabyselection({ row, column }, sheetIndex);

	var rangeTxt = getRangetxt(
		sheetIndex,
		{ row, column },
		chartInfo.currentSheetIndex
	);

	// 调用 controll
	// return chartController.createChart(sheetIndex, chart_id, chartData, [{ row, column }], rangeTxt, width, height, top, left)
}

/**
 * 需要向外提供关闭 websocket 协同连接的API
 */
export function closeWebSocket() {
	server.closeWebSocket();
}
